/* ENSnano, a 3d graphical application for DNA nanostructures. Copyright (C) 2021 Nicolas Levy and Nicolas Schabanel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ //! `ensnano` is a software for designing 3D DNA nanostructures. //! //! # Organization of the software //! //! //! The [main](main) function owns the event_loop and the framebuffer. It recieves window events //! and handles the framebuffer. //! //! ## Drawing process //! //! On each redraw request, the [main](main) funtion generates a new frame, and ask the //! [Multiplexer](multiplexer) to draw on a view of that texture. //! //! The [Multiplexer](multiplexer) knows how the window is devided into several regions. For each //! of these region it knows what application or gui component should draw on it. //! For each region the [Multiplexer](multiplexer) holds a texture, and at each draw request, it //! will request the corresponding app or gui element to possibly update the texture. //! //! Applications are notified when the design that they display have been modified and may request //! from the [Design](design) the data that they need to display it. //! //! ## Handling of events //! //! Window events are recieved by the `main` function that forwards them to the //! [Multiplexer](multiplexer). The [Multiplexer](multiplexer) then forwards the event to the last //! active region (the region under the cursor). Special events like resizing of the window are //! handled by the multiplexer. //! //! When applications and GUI component handle an event. This event might have consequences that //! must be known by the other components of the software. //! //! In the case of an application, the //! consequences is transmitted to the [Mediator](mediator). The [Mediator](mediator) may then //! request appropriate modifications of the [Designs](design) or forward messages for the GUI //! components. //! //! In the case of a GUI component, consequences are transmitted to the [main](main) function that //! will consequently send the appropriate request to the [Mediator](mediator). use std::collections::VecDeque; use std::env; use std::path::PathBuf; use std::rc::Rc; use std::sync::{Arc, Mutex, RwLock}; use std::time::{Duration, Instant}; pub type PhySize = iced_winit::winit::dpi::PhysicalSize; use iced_native::Event as IcedEvent; use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; use iced_winit::{conversion, futures, program, winit, Debug, Size}; use futures::task::SpawnExt; use winit::{ dpi::{PhysicalPosition, PhysicalSize}, event::{Event, ModifiersState, WindowEvent}, event_loop::{ControlFlow, EventLoop}, window::Window, }; #[macro_use] extern crate serde_derive; extern crate serde; #[cfg(not(test))] const MUST_TEST: bool = false; #[cfg(test)] const MUST_TEST: bool = true; mod consts; /// Design handling mod design; /// Graphical interface drawing mod gui; use design::Design; /// Message passing between applications mod mediator; /// Separation of the window into drawing regions mod multiplexer; /// 3D scene drawing mod scene; use mediator::{ActionMode, Mediator, Operation, ParameterPtr, Scheduler, SelectionMode}; mod flatscene; mod text; mod utils; // mod grid_panel; We don't use the grid panel atm use flatscene::FlatScene; use gui::{ColorOverlay, KeepProceed, OverlayType, Requests}; use multiplexer::{DrawArea, ElementType, Multiplexer, Overlay, SplitMode}; use scene::Scene; fn convert_size(size: PhySize) -> Size { Size::new(size.width as f32, size.height as f32) } fn convert_size_u32(size: PhySize) -> Size { Size::new(size.width, size.height) } /// Main function. Runs the event loop and holds the framebuffer. /// /// # Intialization /// /// Before running the event loop, the main fuction do the following /// /// * It request a connection to the GPU and crates a framebuffer /// * It initializes a multiplexer. /// * It initializes applications and GUI component, and associate region of the screen to these /// components /// * It initialized the [Mediator](mediator), the [Scheduler](mediator::Scheduler) and the [Gui /// manager](gui::Gui) /// /// # EventLoop /// /// * The event loop wait for an event. If no event is recieved during 33ms, a new redraw is /// requested. /// * When a event is recieved, it is forwareded to the multiplexer. The Multiplexer may then /// convert this event into a event for a specific screen region. /// * When all window event have been handled, the main function reads messages that it recieved /// from the [Gui Manager](gui::Gui). The consequence of these messages are forwarded to the /// applications. /// * The main loops then reads the messages that it recieved from the [Mediator](mediator) and /// forwards their consequences to the Gui components. /// * Finally, a redraw is requested. /// /// fn main() { // parse arugments, if an argument was given it is treated as a file to open let args: Vec = env::args().collect(); let path = if args.len() >= 2 { Some(PathBuf::from(&args[1])) } else { None }; // Initialize winit let event_loop = EventLoop::new(); let window = winit::window::Window::new(&event_loop).unwrap(); window.set_title("ENSnano"); window.set_min_inner_size(Some(PhySize::new(100, 100))); println!("scale factor {}", window.scale_factor()); let modifiers = ModifiersState::default(); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let surface = unsafe { instance.create_surface(&window) }; // Initialize WGPU let (device, queue) = futures::executor::block_on(async { let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::LowPower, compatible_surface: Some(&surface), }) .await .expect("Could not get adapter\n This might be because gpu drivers are missing. \n You need Vulkan, Metal (for MacOS) or DirectX (for Windows) drivers to run this software"); adapter .request_device( &wgpu::DeviceDescriptor { features: wgpu::Features::empty(), limits: wgpu::Limits::default(), label: None, }, None, ) .await .expect("Request device") }); let format = wgpu::TextureFormat::Bgra8UnormSrgb; let mut swap_chain = { let size = window.inner_size(); device.create_swap_chain( &surface, &wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::RENDER_ATTACHMENT, format, width: size.width, height: size.height, present_mode: wgpu::PresentMode::Mailbox, }, ) }; let settings = Settings { antialiasing: Some(iced_graphics::Antialiasing::MSAAx4), default_text_size: gui::UiSize::Medium.main_text(), default_font: Some(include_bytes!("../font/ensnano2.ttf")), ..Default::default() }; let mut renderer = Renderer::new(Backend::new(&device, settings.clone())); let device = Rc::new(device); let queue = Rc::new(queue); let mut resized = false; let mut scale_factor_changed = false; let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); let mut local_pool = futures::executor::LocalPool::new(); // Initialize the mediator let requests = Arc::new(Mutex::new(Requests::new())); let messages = Arc::new(Mutex::new(IcedMessages::new())); let computing = Arc::new(Mutex::new(false)); let mediator = Arc::new(Mutex::new(Mediator::new( messages.clone(), computing.clone(), ))); let scheduler = Arc::new(Mutex::new(Scheduler::new())); // Initialize the layout let mut multiplexer = Multiplexer::new( window.inner_size(), window.scale_factor(), device.clone(), requests.clone(), ); multiplexer.change_split(SplitMode::Both); // Initialize the scenes let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); let scene_area = multiplexer.get_element_area(ElementType::Scene).unwrap(); let scene = Arc::new(Mutex::new(Scene::new( device.clone(), queue.clone(), window.inner_size(), scene_area, mediator.clone(), &mut encoder, ))); queue.submit(Some(encoder.finish())); mediator .lock() .unwrap() .add_application(scene.clone(), ElementType::Scene); scheduler .lock() .unwrap() .add_application(scene.clone(), ElementType::Scene); let flat_scene = Arc::new(Mutex::new(FlatScene::new( device.clone(), queue.clone(), window.inner_size(), scene_area, mediator.clone(), ))); mediator .lock() .unwrap() .add_application(flat_scene.clone(), ElementType::FlatScene); scheduler .lock() .unwrap() .add_application(flat_scene.clone(), ElementType::FlatScene); // Add a design to the scene if one was given as a command line arguement if let Some(ref path) = path { let design = Design::new_with_path(0, path).unwrap_or_else(|| Design::new(0)); if let Some(tree) = design.get_organizer_tree() { messages.lock().unwrap().push_new_tree(tree) } mediator .lock() .unwrap() .add_design(Arc::new(RwLock::new(design))); } else { let design = Design::new(0); mediator .lock() .unwrap() .add_design(Arc::new(RwLock::new(design))); } // Initialize the UI let mut gui = gui::Gui::new( device.clone(), &window, &multiplexer, requests.clone(), settings, ); let mut overlay_manager = OverlayManager::new(requests.clone(), &window, &mut renderer); // Run event loop let mut last_render_time = std::time::Instant::now(); let mut mouse_interaction = iced::mouse::Interaction::Pointer; let mut icon = None; event_loop.run(move |event, _, control_flow| { // Wait for event or redraw a frame every 33 ms (30 frame per seconds) *control_flow = ControlFlow::WaitUntil(Instant::now() + Duration::from_millis(33)); //*control_flow = ControlFlow::Wait; match event { Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => save_before_quit(requests.clone()), Event::WindowEvent { event: WindowEvent::ModifiersChanged(modifiers), .. } => { multiplexer.update_modifiers(modifiers.clone()); mediator.lock().unwrap().update_modifiers(modifiers.clone()); messages.lock().unwrap().update_modifiers(modifiers.clone()); } Event::WindowEvent { event: WindowEvent::KeyboardInput { .. }, .. } | Event::WindowEvent { event: WindowEvent::ReceivedCharacter(_), .. } if gui.has_keyboard_priority() => { if let Event::WindowEvent { event, .. } = event { if let Some(event) = event.to_static() { let event = iced_winit::conversion::window_event( &event, window.scale_factor(), modifiers, ); if let Some(event) = event { gui.forward_event_all(event); } } } } Event::WindowEvent { event, .. } => { //let modifiers = multiplexer.modifiers(); if let Some(event) = event.to_static() { // Feed the event to the multiplexer let (event, icon_opt) = multiplexer.event(event, &mut resized, &mut scale_factor_changed); icon = icon.or(icon_opt); if let Some((event, area)) = event { // pass the event to the area on which it happenened match area { area if area.is_gui() => { let event = iced_winit::conversion::window_event( &event, window.scale_factor(), modifiers, ); if let Some(event) = event { gui.forward_event(area, event); } } ElementType::Overlay(n) => { let event = iced_winit::conversion::window_event( &event, window.scale_factor(), modifiers, ); if let Some(event) = event { overlay_manager.forward_event(event, n); } } area if area.is_scene() => { let cursor_position = multiplexer.get_cursor_position(); scheduler.lock().unwrap().forward_event( &event, area, cursor_position, ) } _ => unreachable!(), } } } } Event::MainEventsCleared => { scale_factor_changed |= multiplexer.check_scale_factor(&window); let mut redraw = resized | scale_factor_changed | icon.is_some(); redraw |= gui.fetch_change(&window, &multiplexer); // getting the file into which downloading the stapples must be done separatly // because the requests lock must first be dropped. let mut download_stapples = None; let mut set_scaffold = None; let mut stapples = None; let mut blocking_info = None; // When there is no more event to deal with if let Ok(mut requests) = requests.try_lock() { if requests.fitting { mediator.lock().unwrap().request_fits(); requests.fitting = false; } if let Some(ref path) = requests.file_add.take() { let design = Design::new_with_path(0, path); let path_end = formated_path_end(path); if let Some(design) = design { window.set_title(&format!("ENSnano: {}", path_end)); messages.lock().unwrap().notify_new_design(); if let Some(tree) = design.get_organizer_tree() { messages.lock().unwrap().push_new_tree(tree) } mediator.lock().unwrap().clear_designs(); let design = Arc::new(RwLock::new(design)); mediator.lock().unwrap().add_design(design); } } if requests.file_clear { mediator.lock().unwrap().clear_designs(); requests.file_clear = false; } if let Some((path, keep_proceed)) = requests.file_save.take() { let path_end = formated_path_end(&path); window.set_title(&format!("ENSnano: {}", path_end)); mediator.lock().unwrap().save_design(&path); requests.keep_proceed = keep_proceed; } if let Some(value) = requests.toggle_text { mediator.lock().unwrap().toggle_text(value); requests.toggle_text = None; } if let Some(value) = requests.toggle_scene { multiplexer.change_split(value); scheduler .lock() .unwrap() .forward_new_size(window.inner_size(), &multiplexer); gui.resize(&multiplexer, &window); requests.toggle_scene = None; } if requests.make_grids { mediator.lock().unwrap().make_grids(); requests.make_grids = false } if let Some(grid_type) = requests.new_grid.take() { scene.lock().unwrap().make_new_grid(grid_type); } if let Some(selection_mode) = requests.selection_mode { mediator .lock() .unwrap() .change_selection_mode(selection_mode); requests.selection_mode = None; } if let Some(action_mode) = requests.action_mode.take() { println!("action mode {:?}", action_mode); mediator.lock().unwrap().change_action_mode(action_mode); } if let Some(sequence) = requests.sequence_change.take() { mediator.lock().unwrap().change_sequence(sequence); } if let Some(color) = requests.strand_color_change { mediator.lock().unwrap().change_strand_color(color); requests.strand_color_change = None; } if let Some(sensitivity) = requests.scroll_sensitivity.take() { mediator.lock().unwrap().change_sensitivity(sensitivity); //flat_scene.lock().unwrap().change_sensitivity(sensitivity); } if let Some(overlay_type) = requests.overlay_closed.take() { overlay_manager.rm_overlay(overlay_type, &mut multiplexer); } if let Some(overlay_type) = requests.overlay_opened.take() { overlay_manager.add_overlay(overlay_type, &mut multiplexer); } if let Some(op) = requests.operation_update.take() { mediator.lock().unwrap().update_pending(op) } if let Some(b) = requests.toggle_persistent_helices.take() { mediator.lock().unwrap().set_persistent_phantom(b) } if let Some(b) = requests.small_spheres.take() { println!("requested small spheres"); mediator.lock().unwrap().set_small_spheres(b) } if let Some(point) = requests.camera_target.take() { mediator.lock().unwrap().set_camera_target(point) } if let Some(rotation) = requests.camera_rotation.take() { mediator.lock().unwrap().request_camera_rotation(rotation) } if let Some(scaffold_id) = requests.set_scaffold_id.take() { mediator.lock().unwrap().set_scaffold(scaffold_id) } if let Some((sequence, shift)) = requests.scaffold_sequence.take() { set_scaffold = Some((sequence, shift)); } if requests.stapples_request { requests.stapples_request = false; stapples = Some(()); } if requests.recolor_stapples { requests.recolor_stapples = false; mediator.lock().unwrap().recolor_stapples(); } if requests.clean_requests { requests.clean_requests = false; mediator.lock().unwrap().clean_designs(); } if let Some(roll_request) = requests.roll_request.take() { mediator.lock().unwrap().roll_request(roll_request); } if let Some(b) = requests.show_torsion_request.take() { mediator.lock().unwrap().show_torsion_request(b) } if let Some(fog) = requests.fog.take() { scene.lock().unwrap().fog_request(fog) } if let Some(hyperboloid) = requests.new_hyperboloid.take() { use crate::design::Hyperboloid; let h = Hyperboloid { radius: hyperboloid.radius, length: hyperboloid.length, shift: hyperboloid.shift, radius_shift: hyperboloid.radius_shift, forced_radius: None, }; scene.lock().unwrap().make_hyperboloid(h) } if let Some(hyperboloid) = requests.hyperboloid_update.take() { mediator.lock().unwrap().hyperboloid_update(hyperboloid) } if requests.finalize_hyperboloid { requests.finalize_hyperboloid = false; mediator.lock().unwrap().finalize_hyperboloid(); } if requests.cancel_hyperboloid { requests.cancel_hyperboloid = false; mediator.lock().unwrap().cancel_hyperboloid(); } if let Some(roll) = requests.helix_roll.take() { mediator.lock().unwrap().roll_helix(roll) } if requests.copy { mediator.lock().unwrap().request_copy(); requests.copy = false; } if requests.paste { mediator.lock().unwrap().request_pasting_mode(); requests.paste = false; requests.duplication = false; } else if requests.duplication { mediator.lock().unwrap().request_duplication(); requests.duplication = false; } if let Some(b) = requests.rigid_grid_simulation.take() { mediator.lock().unwrap().rigid_grid_request(b); } if let Some(b) = requests.rigid_helices_simulation.take() { mediator.lock().unwrap().rigid_helices_request(b); } if let Some(p) = requests.rigid_body_parameters.take() { mediator.lock().unwrap().rigid_parameters_request(p); } if requests.anchor { mediator.lock().unwrap().request_anchor(); requests.anchor = false; } if let Some(proceed) = requests.keep_proceed.take() { match proceed { KeepProceed::CustomScaffold => { messages.lock().unwrap().push_custom_scaffold() } KeepProceed::DefaultScaffold => { messages.lock().unwrap().push_default_scaffold() } KeepProceed::OptimizeShift(d_id) => { mediator.lock().unwrap().optimize_shift(d_id); } KeepProceed::Stapples(d_id) => { download_stapples = Some(d_id); } KeepProceed::Quit => { *control_flow = ControlFlow::Exit; } KeepProceed::SaveBeforeOpen => { messages .lock() .unwrap() .push_save(Some(KeepProceed::LoadDesignAfterSave)); } KeepProceed::SaveBeforeNew => { messages .lock() .unwrap() .push_save(Some(KeepProceed::NewDesignAfterSave)); } KeepProceed::SaveBeforeQuit => { messages.lock().unwrap().push_save(Some(KeepProceed::Quit)); } KeepProceed::LoadDesign => { messages.lock().unwrap().push_open(); } KeepProceed::LoadDesignAfterSave => { blocking_info = Some(("Save successfully", KeepProceed::LoadDesign)); } KeepProceed::NewDesign => { let design = Design::new(0); messages.lock().unwrap().notify_new_design(); mediator.lock().unwrap().clear_designs(); mediator .lock() .unwrap() .add_design(Arc::new(RwLock::new(design))); } KeepProceed::NewDesignAfterSave => { blocking_info = Some(("Save successfully", KeepProceed::NewDesign)); } _ => (), } } if let Some((d_id, path)) = requests.stapples_file.take() { mediator.lock().unwrap().proceed_stapples(d_id, path); } if let Some(content) = requests.sequence_input.take() { messages.lock().unwrap().push_sequence(content); } if let Some(f) = requests.new_shift_hyperboloid.take() { mediator.lock().unwrap().new_shift_hyperboloid(f); } if let Some(s) = requests.organizer_selection.take() { mediator.lock().unwrap().organizer_selection(s); } if let Some(c) = requests.organizer_candidates.take() { mediator.lock().unwrap().organizer_candidates(c); } if let Some((a, elts)) = requests.new_attribute.take() { mediator.lock().unwrap().update_attribute(a, elts); } if let Some(tree) = requests.new_tree.take() { mediator.lock().unwrap().update_tree(tree); } if let Some(ui_size) = requests.new_ui_size.take() { gui.new_ui_size(ui_size.clone(), &window, &multiplexer); multiplexer.change_ui_size(ui_size.clone(), &window); messages.lock().unwrap().new_ui_size(ui_size); resized = true; } if requests.oxdna { mediator.lock().unwrap().oxdna_export(); requests.oxdna = false; } if requests.split2d { mediator.lock().unwrap().split_2d(); requests.split2d = false; } if requests.all_visible { mediator.lock().unwrap().make_everything_visible(); requests.all_visible = false; } if let Some(b) = requests.toggle_visibility.take() { mediator.lock().unwrap().toggle_visibility(b); } if let Some(b) = requests.redim_2d_helices.take() { mediator.lock().unwrap().redim_2d_helices(b); } if let Some(b) = requests.invert_scroll.take() { multiplexer.invert_y_scroll = b; } if requests.stop_roll { mediator.lock().unwrap().stop_roll(); requests.stop_roll = false; } if requests.toggle_widget { requests.toggle_widget = false; mediator.lock().unwrap().toggle_widget(); } if requests.delete_selection { requests.delete_selection = false; mediator.lock().unwrap().delete_selection(); } if requests.select_scaffold.take().is_some() { mediator.lock().unwrap().select_scaffold(); } if let Some(n) = requests.scaffold_shift.take() { mediator.lock().unwrap().set_scaffold_shift(n); } if let Some(mode) = requests.rendering_mode.take() { mediator.lock().unwrap().rendering_mode(mode); } if let Some(bg) = requests.background3d.take() { mediator.lock().unwrap().background3d(bg); } if requests.undo.take().is_some() { mediator.lock().unwrap().undo() } if requests.redo.take().is_some() { mediator.lock().unwrap().redo() } if requests.save_shortcut.take().is_some() { messages.lock().unwrap().push_save(None); } if requests.show_tutorial.take().is_some() { messages.lock().unwrap().push_show_tutorial() } if requests.force_help.take().is_some() { messages.lock().unwrap().show_help() } } if let Some((msg, keep_proceed)) = blocking_info.take() { crate::utils::blocking_message( msg.into(), rfd::MessageLevel::Info, requests.clone(), keep_proceed, ) } if let Some(d_id) = download_stapples { let requests = requests.clone(); let dialog = rfd::AsyncFileDialog::new().save_file(); std::thread::spawn(move || { let save_op = async move { let file = dialog.await; if let Some(handle) = file { let mut path_buf: std::path::PathBuf = handle.path().clone().into(); path_buf.set_extension("xlsx"); requests.lock().unwrap().stapples_file = Some((d_id, path_buf)); } }; futures::executor::block_on(save_op); }); } if let Some((sequence, shift)) = set_scaffold.take() { mediator.lock().unwrap().set_scaffold_sequence( sequence, requests.clone(), shift, ); } if stapples.take().is_some() { mediator.lock().unwrap().download_stapples(requests.clone()) } // Treat eventual event that happenend in the gui left panel. let _overlay_change = overlay_manager.fetch_change(&multiplexer, &window, &mut renderer); { let mut messages = messages.lock().unwrap(); gui.forward_messages(&mut messages); overlay_manager.forward_messages(&mut messages); } mediator.lock().unwrap().observe_designs(); let now = std::time::Instant::now(); let dt = now - last_render_time; redraw |= scheduler.lock().unwrap().check_redraw(&multiplexer, dt); last_render_time = now; if redraw { window.request_redraw(); } } Event::RedrawRequested(_) if window.inner_size().width > 0 && window.inner_size().height > 0 => { if resized { multiplexer.generate_textures(); scheduler .lock() .unwrap() .forward_new_size(window.inner_size(), &multiplexer); let window_size = window.inner_size(); swap_chain = device.create_swap_chain( &surface, &wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::RENDER_ATTACHMENT, format, width: window_size.width, height: window_size.height, present_mode: wgpu::PresentMode::Mailbox, }, ); gui.resize(&multiplexer, &window); } if scale_factor_changed { multiplexer.generate_textures(); gui.notify_scale_factor_change(&window, &multiplexer); println!("lolz"); scheduler .lock() .unwrap() .forward_new_size(window.inner_size(), &multiplexer); let window_size = window.inner_size(); swap_chain = device.create_swap_chain( &surface, &wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::RENDER_ATTACHMENT, format, width: window_size.width, height: window_size.height, present_mode: wgpu::PresentMode::Mailbox, }, ); gui.resize(&multiplexer, &window); } // Get viewports from the partition // If there are events pending gui.update(&multiplexer, &window); overlay_manager.process_event(&mut renderer, resized, &multiplexer, &window); resized = false; scale_factor_changed = false; if let Ok(frame) = swap_chain.get_current_frame() { let mut encoder = device .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); // We draw the applications first let now = std::time::Instant::now(); let dt = now - last_render_time; scheduler .lock() .unwrap() .draw_apps(&mut encoder, &multiplexer, dt); gui.render( &mut encoder, &window, &multiplexer, &mut staging_belt, &mut mouse_interaction, ); multiplexer.draw(&mut encoder, &frame.output.view); //overlay_manager.render(&device, &mut staging_belt, &mut encoder, &frame.output.view, &multiplexer, &window, &mut renderer); // Then we submit the work staging_belt.finish(); queue.submit(Some(encoder.finish())); // And update the mouse cursor window.set_cursor_icon(iced_winit::conversion::mouse_interaction( mouse_interaction, )); if let Some(icon) = icon.take() { window.set_cursor_icon(icon); } local_pool .spawner() .spawn(staging_belt.recall()) .expect("Recall staging buffers"); local_pool.run_until_stalled(); } else { println!("Error getting next frame, attempt to recreate swap chain"); resized = true; } } _ => {} } }) } /// Message sent to the gui component pub struct IcedMessages { left_panel: VecDeque, top_bar: VecDeque, color_overlay: VecDeque, status_bar: VecDeque, application_state: ApplicationState, } #[derive(Default, PartialEq, Eq, Debug, Clone)] pub struct ApplicationState { pub can_undo: bool, pub can_redo: bool, pub simulation_state: crate::design::SimulationState, pub parameter_ptr: ParameterPtr, pub axis_aligned: bool, pub action_mode: ActionMode, pub selection_mode: SelectionMode, } impl IcedMessages { #[allow(clippy::new_without_default)] pub fn new() -> Self { Self { left_panel: VecDeque::new(), top_bar: VecDeque::new(), color_overlay: VecDeque::new(), status_bar: VecDeque::new(), application_state: Default::default(), } } pub fn push_scaffold_info(&mut self, info: Option) { self.left_panel .push_back(gui::left_panel::Message::NewScaffoldInfo(info)); } pub fn push_custom_scaffold(&mut self) { self.left_panel .push_back(gui::left_panel::Message::CustomScaffoldRequested); } pub fn push_default_scaffold(&mut self) { self.left_panel .push_back(gui::left_panel::Message::DeffaultScaffoldRequested); } pub fn push_color(&mut self, color: u32) { let bytes = color.to_be_bytes(); // bytes is [A, R, G, B] let color = iced::Color::from_rgb8(bytes[1], bytes[2], bytes[3]); self.color_overlay .push_back(gui::left_panel::ColorMessage::StrandColorChanged(color)); self.left_panel .push_back(gui::left_panel::Message::StrandColorChanged(color)); } pub fn push_sequence(&mut self, sequence: String) { self.left_panel .push_back(gui::left_panel::Message::SequenceChanged(sequence)); } pub fn push_op(&mut self, operation: Arc) { self.status_bar .push_back(gui::status_bar::Message::Operation(operation)); } pub fn push_selection(&mut self, selection: mediator::Selection, values: Vec) { self.left_panel .push_back(gui::left_panel::Message::Selection( selection, values.clone(), )) } pub fn push_candidate(&mut self, selection: mediator::Selection, values: Vec) { self.status_bar .push_back(gui::status_bar::Message::Selection( selection, values.clone(), )); } pub fn push_organizer_selection(&mut self, selection: Vec) { self.left_panel .push_back(gui::left_panel::Message::NewSelection(selection)) } pub fn clear_op(&mut self) { self.status_bar.push_back(gui::status_bar::Message::ClearOp); } pub fn push_action_mode(&mut self, action_mode: mediator::ActionMode) { self.left_panel .push_back(gui::left_panel::Message::ActionModeChanged(action_mode)) } pub fn push_selection_mode(&mut self, selection_mode: mediator::SelectionMode) { self.left_panel .push_back(gui::left_panel::Message::SelectionModeChanged( selection_mode, )) } pub fn push_progress(&mut self, progress_name: String, progress: f32) { self.status_bar .push_back(gui::status_bar::Message::Progress(Some(( progress_name, progress, )))) } pub fn finish_progess(&mut self) { self.status_bar .push_back(gui::status_bar::Message::Progress(None)) } pub fn notify_new_design(&mut self) { self.left_panel .push_back(gui::left_panel::Message::NewDesign) } pub fn push_roll(&mut self, roll: f32) { self.left_panel .push_back(gui::left_panel::Message::HelixRoll(roll)) } pub fn push_dna_elements(&mut self, elements: Vec) { self.left_panel .push_back(gui::left_panel::Message::NewDnaElement(elements)) } pub fn update_modifiers(&mut self, modifiers: ModifiersState) { self.left_panel .push_back(gui::left_panel::Message::ModifiersChanged(modifiers)) } pub fn push_new_tree( &mut self, tree: ensnano_organizer::OrganizerTree, ) { self.left_panel .push_back(gui::left_panel::Message::NewTreeApp(tree)) } pub fn new_ui_size(&mut self, ui_size: gui::UiSize) { self.left_panel .push_back(gui::left_panel::Message::UiSizeChanged(ui_size.clone())); self.top_bar .push_back(gui::top_bar::Message::UiSizeChanged(ui_size.clone())); } pub fn push_can_make_grid(&mut self, can_make_grid: bool) { self.left_panel .push_back(gui::left_panel::Message::CanMakeGrid(can_make_grid)); } pub fn push_save(&mut self, keep_proceed: Option) { self.top_bar .push_back(gui::top_bar::Message::FileSaveRequested(keep_proceed)); } pub fn push_open(&mut self) { self.top_bar .push_back(gui::top_bar::Message::FileAddRequested); } pub fn push_show_tutorial(&mut self) { self.left_panel .push_back(gui::left_panel::Message::ShowTutorial); } pub fn show_help(&mut self) { self.left_panel .push_back(gui::left_panel::Message::ForceHelp); } pub(crate) fn push_application_state(&mut self, state: ApplicationState) { let must_update = self.application_state != state; self.application_state = state.clone(); if must_update { self.left_panel .push_back(gui::left_panel::Message::NewApplicationState(state.clone())); self.top_bar .push_back(gui::top_bar::Message::NewApplicationState(state)) } } } pub struct OverlayManager { color_state: iced_native::program::State, color_debug: Debug, overlay_types: Vec, overlays: Vec, } impl OverlayManager { pub fn new(requests: Arc>, window: &Window, renderer: &mut Renderer) -> Self { let color = ColorOverlay::new( requests.clone(), PhysicalSize::new(250., 250.).to_logical(window.scale_factor()), ); let mut color_debug = Debug::new(); let color_state = program::State::new( color, convert_size(PhysicalSize::new(250, 250)), conversion::cursor_position(PhysicalPosition::new(-1f64, -1f64), window.scale_factor()), renderer, &mut color_debug, ); Self { color_state, color_debug, overlay_types: Vec::new(), overlays: Vec::new(), } } fn forward_event(&mut self, event: IcedEvent, n: usize) { match self.overlay_types.get(n) { None => { println!("recieve event from non existing overlay"); unreachable!(); } Some(OverlayType::Color) => self.color_state.queue_event(event), } } fn add_overlay(&mut self, overlay_type: OverlayType, multiplexer: &mut Multiplexer) { match overlay_type { OverlayType::Color => self.overlays.push(Overlay { position: PhysicalPosition::new(500, 500), size: PhysicalSize::new(250, 250), }), } self.overlay_types.push(overlay_type); self.update_multiplexer(multiplexer); } fn process_event( &mut self, renderer: &mut Renderer, resized: bool, multiplexer: &Multiplexer, window: &Window, ) { for (n, overlay) in self.overlay_types.iter().enumerate() { let cursor_position = if multiplexer.foccused_element() == Some(ElementType::Overlay(n)) { multiplexer.get_cursor_position() } else { PhysicalPosition::new(-1., -1.) }; let mut clipboard = iced_native::clipboard::Null; match overlay { OverlayType::Color => { if !self.color_state.is_queue_empty() || resized { let _ = self.color_state.update( convert_size(PhysicalSize::new(250, 250)), conversion::cursor_position(cursor_position, window.scale_factor()), renderer, &mut clipboard, &mut self.color_debug, ); } } } } } #[allow(dead_code)] fn render( &self, device: &wgpu::Device, staging_belt: &mut wgpu::util::StagingBelt, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, multiplexer: &Multiplexer, window: &Window, renderer: &mut Renderer, ) { for overlay_type in self.overlay_types.iter() { match overlay_type { OverlayType::Color => { let color_viewport = Viewport::with_physical_size( convert_size_u32(multiplexer.window_size), window.scale_factor(), ); let _color_interaction = renderer.backend_mut().draw( &device, staging_belt, encoder, &target, &color_viewport, self.color_state.primitive(), &self.color_debug.overlay(), ); } } } } fn rm_overlay(&mut self, overlay_type: OverlayType, multiplexer: &mut Multiplexer) { let mut rm_idx = Vec::new(); for (idx, overlay_type_) in self.overlay_types.iter().rev().enumerate() { if *overlay_type_ == overlay_type { rm_idx.push(idx); } } for idx in rm_idx.iter() { self.overlays.remove(*idx); self.overlay_types.remove(*idx); } self.update_multiplexer(multiplexer); } fn update_multiplexer(&self, multiplexer: &mut Multiplexer) { multiplexer.set_overlays(self.overlays.clone()) } fn forward_messages(&mut self, messages: &mut IcedMessages) { for m in messages.color_overlay.drain(..) { self.color_state.queue_message(m); } } fn fetch_change( &mut self, multiplexer: &Multiplexer, window: &Window, renderer: &mut Renderer, ) -> bool { let mut ret = false; for (n, overlay) in self.overlay_types.iter().enumerate() { let cursor_position = if multiplexer.foccused_element() == Some(ElementType::Overlay(n)) { multiplexer.get_cursor_position() } else { PhysicalPosition::new(-1., -1.) }; let mut clipboard = iced_native::clipboard::Null; match overlay { OverlayType::Color => { if !self.color_state.is_queue_empty() { ret = true; let _ = self.color_state.update( convert_size(PhysicalSize::new(250, 250)), conversion::cursor_position(cursor_position, window.scale_factor()), renderer, &mut clipboard, &mut self.color_debug, ); } } } } ret } } fn formated_path_end(path: &PathBuf) -> String { let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect(); let mut ret = if components.len() > 3 { vec!["..."] } else { vec![] }; let mut iter = components.iter().rev().take(3).rev(); for _ in 0..3 { if let Some(comp) = iter.next().and_then(|s| s.to_str()) { ret.push(comp.clone()); } } ret.join("/") } fn save_before_new(requests: Arc>) { crate::utils::yes_no_dialog( "Do you want to save your design before loading an empty one?".into(), requests, KeepProceed::SaveBeforeNew, Some(KeepProceed::NewDesign), ); } fn save_before_open(requests: Arc>) { crate::utils::yes_no_dialog( "Do you want to save your design before loading a new one?".into(), requests, KeepProceed::SaveBeforeOpen, Some(KeepProceed::LoadDesign), ); } fn save_before_quit(requests: Arc>) { crate::utils::yes_no_dialog( "Do you want to save your design before exiting the app?".into(), requests, KeepProceed::SaveBeforeQuit, Some(KeepProceed::Quit), ); }