use crate::widgets::{EditorWidget, WebWidget};
use nightshade::prelude::{egui, window};
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender, channel};
use wry::dpi::{LogicalPosition, LogicalSize};
use wry::{NewWindowResponse, Rect, WebView, WebViewBuilder};
pub struct WebviewState {
webviews: HashMap<String, WebView>,
webview_bounds: HashMap<String, (f64, f64, f64, f64)>,
window: Option<Arc<window::Window>>,
new_window_sender: Sender<String>,
new_window_receiver: Receiver<String>,
}
impl Default for WebviewState {
fn default() -> Self {
let (sender, receiver) = channel();
Self {
webviews: HashMap::new(),
webview_bounds: HashMap::new(),
window: None,
new_window_sender: sender,
new_window_receiver: receiver,
}
}
}
impl WebviewState {
pub fn set_window(&mut self, window: Arc<window::Window>) {
self.window = Some(window);
}
pub fn create_webview(&mut self, id: String, url: &str, rect: egui::Rect) {
if self.webviews.contains_key(&id) {
return;
}
let Some(window) = &self.window else {
return;
};
let bounds = Rect {
position: LogicalPosition::new(rect.min.x as f64, rect.min.y as f64).into(),
size: LogicalSize::new(rect.width() as f64, rect.height() as f64).into(),
};
let sender = self.new_window_sender.clone();
let webview_result = WebViewBuilder::new()
.with_url(url)
.with_bounds(bounds)
.with_navigation_handler(|_| true)
.with_new_window_req_handler(move |url, _| {
let _ = sender.send(url);
NewWindowResponse::Deny
})
.build_as_child(window.as_ref());
if let Ok(webview) = webview_result {
let _ = webview.set_visible(true);
self.webview_bounds.insert(
id.clone(),
(
rect.min.x as f64,
rect.min.y as f64,
rect.width() as f64,
rect.height() as f64,
),
);
self.webviews.insert(id, webview);
}
}
pub fn update_position(&mut self, id: &str, rect: egui::Rect) {
let Some(webview) = self.webviews.get(id) else {
return;
};
let new_bounds = (
rect.min.x as f64,
rect.min.y as f64,
rect.width() as f64,
rect.height() as f64,
);
if let Some(old_bounds) = self.webview_bounds.get(id)
&& *old_bounds == new_bounds
{
return;
}
let bounds = Rect {
position: LogicalPosition::new(new_bounds.0, new_bounds.1).into(),
size: LogicalSize::new(new_bounds.2, new_bounds.3).into(),
};
let _ = webview.set_bounds(bounds);
self.webview_bounds.insert(id.to_string(), new_bounds);
}
pub fn ensure_all_visible(&self) {
for webview in self.webviews.values() {
let _ = webview.set_visible(true);
}
}
pub fn pending_new_windows(&self) -> Vec<String> {
self.new_window_receiver.try_iter().collect()
}
pub fn has_webview(&self, id: &str) -> bool {
self.webviews.contains_key(id)
}
pub fn retain_only(&mut self, active_ids: &[(String, String, egui::Rect)]) {
self.webviews
.retain(|id, _| active_ids.iter().any(|(active_id, _, _)| active_id == id));
self.webview_bounds
.retain(|id, _| active_ids.iter().any(|(active_id, _, _)| active_id == id));
}
}
impl crate::Editor {
pub fn update_webviews(&mut self, world: &mut nightshade::prelude::World) {
if let Some(window_handle) = &world.resources.window.handle {
self.webview_state.set_window(window_handle.clone());
}
self.webview_state
.retain_only(&self.context.ui.web_widget_rects);
for (id, url, rect) in self.context.ui.web_widget_rects.drain(..) {
if !self.webview_state.has_webview(&id) {
self.webview_state.create_webview(id.clone(), &url, rect);
}
self.webview_state.update_position(&id, rect);
}
self.webview_state.ensure_all_visible();
for url in self.webview_state.pending_new_windows() {
let web_widget = WebWidget {
url,
id: nightshade::prelude::uuid::Uuid::new_v4().to_string(),
};
self.mosaic.insert_pane(EditorWidget::Web(web_widget));
}
}
}