use std::{collections::HashMap, sync::Arc};
use tao::{event::{Event, WindowEvent}, event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy}, window::{Window, WindowBuilder, WindowId}};
use wry::{WebView, WebViewBuilder};
use pyo3::prelude::*;
use crate::{executer::FunctionInfo, ipc::eventlistner};
#[macro_export]
macro_rules! unsafe_impl_sync_send {
($type:ty) => {
unsafe impl Send for $type {}
unsafe impl Sync for $type {}
};
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum PythonEventAPI {
UserEvent(String, String),
NoneEvent(String),
}
pub(crate) fn connection_event_emitter(event: &str, pyload:&str) -> PythonEventAPI {
match event {
"ari_event" => PythonEventAPI::UserEvent(((&event)).to_string(), ((&pyload)).to_string()),
_ => PythonEventAPI::NoneEvent(format!("Not Spourted Event: {}", event)), // Rückgabe von None, wenn das Event nicht übereinstimmt
}
}
#[derive(Debug, Clone)]
pub(crate) struct PyProxyEventEmitter {
emitter: EventLoopProxy<PythonEventAPI>,
}
impl PyProxyEventEmitter {
pub fn new(emitter: EventLoopProxy<PythonEventAPI>) -> Self {
Self {
emitter,
}
}
pub(crate) fn send_event(&mut self, event: &str, payload: &str) {
let main_proxy_event = connection_event_emitter(event, payload);
let _ = self.emitter.send_event(main_proxy_event);
}
}
#[pyclass]
pub struct RustAPI {
event_loop:EventLoop<PythonEventAPI>,
emitter: PyProxyEventEmitter
}
#[pymethods]
impl RustAPI {
#[new]
pub fn new()->Self{
let event_loop: EventLoop<PythonEventAPI> = EventLoopBuilder::<PythonEventAPI>::with_user_event().build();
println!("create_eventloop{event_loop:?}");
let proxy = event_loop.create_proxy();
println!("create_proxy{proxy:?}");
let emitter = PyProxyEventEmitter::new(proxy);
println!("create_emitter{emitter:?}");
Self{
event_loop,
emitter
}
}
pub fn send_proxy_event(&mut self, event: &str, pyload: &str)->PyResult<()>{
println!("send_proxy_event input data: {event}{pyload}");
self.emitter.send_event(event, pyload); // to be able to send the data outside the box immediately to the event loop
Ok(())
}
pub fn start(
&mut self,
handler: Option<FunctionInfo>,
initial_script: Option<Vec<String>>,
frontend: Option<String>,
) {
println!("start main thread with the handler: {handler:?}");
let mut webview_runtime_api: HashMap<WindowId, (Window, WebView)> = HashMap::new();
let event_loop = std::mem::replace(
&mut self.event_loop,
EventLoopBuilder::with_user_event().build()
);
let window = WindowBuilder::new().build(&event_loop).unwrap();
let web_view = {
let mut builder = WebViewBuilder::new();
// Conditionally add the IPC handler
if let Some(func) = handler.clone() {
let func = Arc::new(func); // Wrap FunctionInfo in Arc
let handler = eventlistner(window.id(), Some(func));
builder = builder.with_ipc_handler(handler);
}
if let Some(scripts) = initial_script.clone() {
for script in scripts {
builder = builder.with_initialization_script(&script);
}
}
if let Some(script) = frontend.clone() {
if script.starts_with("http") || script.starts_with("https") {
builder = builder.with_url(&script);
} else {
builder = builder.with_html(&script);
}
}
// Platform-specific build logic
#[cfg(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
))]
{
builder.build(&window).unwrap()
}
#[cfg(not(any(
target_os = "windows",
target_os = "macos",
target_os = "ios",
target_os = "android"
)))]
{
use tao::platform::unix::WindowExtUnix;
use wry::WebViewBuilderExtUnix;
let vbox = window.default_vbox().unwrap();
builder.build_gtk(vbox)?
}
};
webview_runtime_api.insert(window.id(), (window, web_view));
let mut webviews_api = webview_runtime_api;
event_loop.run(move |event, _target_loop, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::WindowEvent {
event: WindowEvent::CloseRequested,
window_id,
..
} => handle_close_requested(window_id, &mut webviews_api, control_flow),
Event::UserEvent(ref user_event) => {
match user_event {
PythonEventAPI::UserEvent(event, pyload) => {
println!(
"Some Event from Python [ event Type: {}, payload: {}]",
event, pyload
);
}
PythonEventAPI::NoneEvent(data) => {
println!(
"Not Supported Event from Python: {}",
data
);
}
}
},
_ => (),
}
});
}
}
pub fn handle_close_requested(
window_id: WindowId,
webviews: &mut HashMap<WindowId, (Window, WebView)>,
control_flow: &mut ControlFlow,
) {
webviews.remove(&window_id);
if webviews.is_empty() {
*control_flow = ControlFlow::Exit;
}
}
unsafe_impl_sync_send!(RustAPI);
use std::sync::Arc;
use pyo3::{prelude::*, types::PyDict};
use muda::Menu;
// / The FunctionInfo object contains information about a Python callback for the menu item,
/// in case a callback handler or event is required to be fired.
#[pyclass]
#[derive(Debug, Clone)]
pub struct FunctionInfo {
#[pyo3(get, set)]
pub handler: Py<PyAny>,
#[pyo3(get, set)]
pub is_async: bool,
#[pyo3(get, set)]
pub number_of_params: u8,
#[pyo3(get, set)]
pub args: Py<PyDict>,
#[pyo3(get, set)]
pub kwargs: Py<PyDict>,
}
#[pymethods]
impl FunctionInfo {
#[new]
pub fn new(
handler: Py<PyAny>,
is_async: bool,
number_of_params: u8,
args: Py<PyDict>,
kwargs: Py<PyDict>,
) -> Self {
Self {
handler,
is_async,
number_of_params,
args,
kwargs,
}
}
}
// The PyFrameMenuItem object contains information about a PyFrameMenuItem.
#[pyclass]
#[derive(Debug, Clone)]
pub struct PyFrameMenuItem {
#[pyo3(get, set)]
pub menu_type:Option<String>,
#[pyo3(get, set)]
pub handler:Option<FunctionInfo>,
}
#[pymethods]
impl PyFrameMenuItem {
#[new]
pub fn new(
menu_type:Option<String>,
handler:Option<FunctionInfo>,
) -> Self {
Self {
menu_type,
handler
}
}
}
/// The PyFrameSystemMenu object contains all the information about the menu itself and its items,
/// which is passed to the create_pyframe_menu function on the Rust side to create the menu structure.
#[pyclass]
#[derive(Debug, Clone)]
pub struct PyFrameSystemMenu {
#[pyo3(get, set)]
menus:Vec<PyFrameMenuItem>
}
#[pymethods]
impl PyFrameSystemMenu {
#[new]
pub fn new(
menus:Vec<PyFrameMenuItem>
) -> Self {
Self {
menus
}
}
}
/// has the task to create the menu, with all information from python
pub fn create_pyframe_menu(menu: Option<Arc<Vec<PyFrameMenuItem>>>,)->Menu{
let menu = Menu::new();
menu
}