This commit is contained in:
Tsuki 2024-11-28 16:20:01 +08:00
parent 87e49cae9c
commit 08358ef968
30 changed files with 1709 additions and 522 deletions

8
Cargo.lock generated
View File

@ -311,9 +311,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
version = "1.19.0"
version = "1.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a"
dependencies = [
"bytemuck_derive",
]
@ -665,6 +665,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
name = "element_bridge"
version = "0.1.0"
dependencies = [
"bytemuck",
"flume",
"makepad-widgets",
"mp_elements",
@ -1712,9 +1713,11 @@ name = "mp"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"element_bridge",
"futures",
"glam",
"image",
"indexmap",
"log",
"makepad-widgets",
@ -1764,6 +1767,7 @@ dependencies = [
"quick_cache",
"regex",
"rust-embed",
"thiserror 2.0.3",
"wgpu",
]

View File

@ -9,3 +9,4 @@ makepad-widgets = { git = "https://github.com/makepad/makepad", branch = "rik",
flume = "0.11.1"
wgpu = "23.0.0"
tokio = { version = "1.41.1", features = ["sync"] }
bytemuck = "1.20.0"

View File

@ -9,26 +9,18 @@ pub use mp_elements::app::Window;
#[derive(Clone, Debug)]
pub struct TextureBridge {
dirty: bool,
_buffer: Arc<tokio::sync::Mutex<Vec<u8>>>,
window: Arc<RenderWindow>,
_texture: Texture,
}
impl TextureBridge {
pub fn new(texture: Texture, app: &App, window: Window) -> Self {
pub fn new(app: &App, window: Window) -> Self {
let window = app.create_window(window);
Self {
dirty: true,
_buffer: Arc::new(tokio::sync::Mutex::new(vec![0u8; 256 * 256 * 4])),
window: Arc::new(window),
_texture: texture,
}
}
pub fn texture(&self) -> &Texture {
&self._texture
}
pub fn window(&self) -> &RenderWindow {
&self.window
}
@ -41,7 +33,7 @@ impl TextureBridge {
app.draw(&self.window, draw_list).await;
}
pub async fn load_data(buffer: &mut Vec<u8>, ctx: &Ctx, render_window: &RenderWindow) {
pub async fn load_data(ctx: &Ctx, render_window: &RenderWindow) -> Vec<f32> {
let output = &render_window.output().output_buffer;
let (sender, receiver) = flume::bounded(1);
@ -52,18 +44,14 @@ impl TextureBridge {
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
receiver.recv_async().await.unwrap().unwrap();
{
let result = {
let view = slice.get_mapped_range();
buffer.copy_from_slice(&view[..]);
}
let new_vec: Vec<f32> = bytemuck::cast_slice(&view[..]).to_vec();
new_vec
};
output.unmap();
}
pub fn update_texture(&self, cx: &mut Cx, buffer: Vec<u8>) {
self._texture.put_back_vec_u8(cx, buffer, None);
}
pub fn buffer(&self) -> Arc<tokio::sync::Mutex<Vec<u8>>> {
self._buffer.clone()
result
}
}

BIN
image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

@ -21,3 +21,5 @@ futures = "0.3.31"
async-trait = "0.1.83"
thiserror = "2.0.3"
indexmap = "2.6.0"
image = "0.25.5"
bytemuck = "1.20.0"

View File

@ -1,26 +1,22 @@
use crate::file::*;
use crate::menu::{handle_menu, spawn_background};
use crate::widgets::area::TAreaWidgetRefExt;
use crate::widgets::selector::{
ItemKey, ItemValue, SelectorListWidgetRefExt, SelectorWidgetRefExt,
};
use crate::windows_manager::WM;
use crate::{render_task::RenderTasks, PLUGIN_MANAGER, RUNTIME};
use crate::menu::handle_menu;
use crate::selector::SelectorModalAction;
use crate::task_register::{BridgeManager, ConfigManager, TaskConfig, TaskManager};
use crate::widgets::area::{TAreaAction, TAreaWidgetRefExt};
use crate::widgets::loading::{self, LoadingModalWidgetRefExt};
use crate::windows_manager::{WindowId, WindowsManager};
use crate::PLUGIN_MANAGER;
use ::log::info;
use element_bridge::TextureBridge;
use makepad_widgets::makepad_micro_serde::*;
use makepad_widgets::*;
use mp_elements::app::DrawList as DW;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
live_design! {
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::app_ui::MainView;
import crate::widgets::selector::*;
import crate::selector::SelectorBase;
import crate::widgets::loading::*;
HELLO = "Hello, World!";
@ -62,7 +58,13 @@ live_design! {
height: 470
width:650
show_bg: false
selector = <Selector> {}
selector = <SelectorBase> {}
}
}
loading_modal = <Modal> {
content: {
inner_loading = <LoadingModal> {}
}
}
@ -144,15 +146,12 @@ pub enum AppAction {
None,
}
impl AppAction {
pub fn register_task(id: usize, bridge: Arc<TextureBridge>, draw_list: DW) -> AppAction {
AppAction::Task(TaskAction::Register(id, bridge, draw_list))
}
}
#[derive(Debug, Clone)]
pub enum TaskAction {
Register(usize, Arc<TextureBridge>, DW),
Register {
draw_list: DW,
configs: Vec<Option<TaskConfig>>,
},
Unregister(),
}
@ -162,20 +161,33 @@ pub enum FileAction {
OpenFolder(PathBuf),
}
#[derive(Default)]
pub struct WindowState {
pub all_windows_num: usize,
pub current_focus: Option<WindowId>,
}
#[derive(Default)]
pub struct AppState {
pub window_state: WindowState,
}
#[derive(Live, LiveHook)]
pub struct App {
#[live]
ui: WidgetRef,
#[rust]
pub render_tasks: Arc<Mutex<RenderTasks>>,
task_manager: TaskManager,
#[rust]
windows_manager: WM,
windows_manager: WindowsManager,
#[rust]
tex_reciver: ToUIReceiver<Vec<u8>>,
bridge_manager: BridgeManager,
#[rust]
file_manager: FileManager,
#[rust]
config_manager: ConfigManager,
#[rust]
app_state: AppState,
#[rust]
timer: Timer,
}
@ -188,56 +200,45 @@ impl LiveRegister for App {
crate::widgets::renderer::live_design(_cx);
crate::widgets::background_text::live_design(_cx);
crate::widgets::selector::live_design(_cx);
crate::widgets::loading::live_design(_cx);
crate::selector::live_design(_cx);
crate::widgets::element_panels::live_design(_cx);
crate::sidepanel::live_design(_cx);
}
}
#[derive(SerRon, DeRon)]
struct AppStateRon {
slide: usize,
}
impl MatchEvent for App {
// Start UP
fn handle_startup(&mut self, cx: &mut Cx) {
info!("Starting up......");
let ui = self.ui.clone();
// New Area init
let area = ui.tarea(id!(quad));
area.set_windows(self.windows_manager.clone());
// Listening to the render tasks
let render_tasks = self.render_tasks.clone();
RUNTIME.spawn(async move {
let render_task = render_tasks.lock().await;
render_task.render().await;
});
let id = self.windows_manager.add_window(area.clone());
area.set_id(id.clone());
let items = vec![(
ItemKey {
path: "path".to_string(),
category: "category".to_string(),
description: "description_1".to_string(),
},
vec![
ItemValue {
name: "Item 1".to_string(),
icon: "icon_radar.svg".to_string(),
},
ItemValue {
name: "Item 2".to_string(),
icon: "icon_threed.svg".to_string(),
},
],
)];
// Default Focus
self.app_state.window_state.current_focus = Some(id.clone());
ui.selector(id!(selector)).set_items(cx, items);
// Default Bridge for Primary Renderer
self.bridge_manager.new_bridge(id);
// Connect
self.task_manager.set_sender(self.windows_manager.sender());
info!("Rendering pipelines......");
self.task_manager.run();
}
fn handle_signal(&mut self, cx: &mut Cx) {
self.file_manager.try_deal_file(&self.ui, cx);
self.windows_manager
.try_deal_render_result(cx, &self.bridge_manager);
}
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
let ui = self.ui.clone();
if ui.button(id!(start)).clicked(actions) {
cx.action(AppAction::OpenFile);
}
@ -274,17 +275,37 @@ impl MatchEvent for App {
}
AppAction::Task(task) => match task {
TaskAction::Register(bridge_id, bridge, draw_list) => {
info!(
"Register Task for {:?}, draw_list: {:?}",
bridge_id, draw_list
);
let bridge = bridge.render_window();
let sender = self.tex_reciver.sender();
RUNTIME.block_on(async {
let mut render_tasks = self.render_tasks.lock().await;
render_tasks.register_task(sender, bridge_id, bridge, draw_list);
});
TaskAction::Register { draw_list, configs } => {
info!("Register Task for draw_list");
let current_window = &self.app_state.window_state.current_focus;
if let Some(window) = current_window.as_ref() {
let bridge = self
.bridge_manager
.get_bridge(window)
.expect("Bridge not found");
let render_window = bridge.render_window();
if let Ok(id) = self.task_manager.register_task_for_drawlist(
draw_list,
render_window,
configs.clone(),
) {
info!("Task Registered");
// Add Task Config to Config Manager
self.config_manager.register_config(id, configs);
// Create Net for the Task
self.bridge_manager.create_net(id, window.clone());
// Redraw the window
self.task_manager.redraw();
} else {
error!("Task Register Failed");
}
} else {
info!("No window found");
}
}
TaskAction::Unregister() => {
@ -296,6 +317,13 @@ impl MatchEvent for App {
FileAction::OpenFile(file) => {
info!("Open File: {:?}", file);
self.file_manager.load_data(&file);
let loading_modal = ui.modal(id!(loading_modal));
let inner_loading = loading_modal.loading_modal(id!(inner_loading));
inner_loading
.set_state(_cx, loading::LoadingModalState::BackwardsPaginateUntilEvent);
loading_modal.open(_cx);
}
FileAction::OpenFolder(folder) => {
@ -313,16 +341,47 @@ impl MatchEvent for App {
_ => {}
}
match e.as_widget_action().cast() {
SelectorModalAction::Close => {
let selector_modal = ui.modal(id!(selector_modal));
selector_modal.close(_cx);
}
SelectorModalAction::RegisterTask {
elements,
data,
config,
} => {
if let Ok(draw_list) = self.task_manager.get_draw_list(&data, elements, &config) {
_cx.action(AppAction::Task(TaskAction::Register {
draw_list,
configs: config,
}));
} else {
error!("Draw List not found");
}
}
_ => {}
}
match e.as_widget_action().cast() {
TAreaAction::GetFocus(id) => {
info!("Get Focus: {:?}", id);
self.app_state.window_state.current_focus = Some(id);
}
_ => {}
}
}
}
impl AppMain for App {
fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
self.match_event(cx, event);
self.ui.handle_event(cx, event, &mut Scope::empty());
spawn_background(self, event);
handle_menu(cx, event);
let scope = &mut Scope::with_data(&mut self.app_state);
self.ui.handle_event(cx, event, scope);
handle_menu(cx, event);
self.timer.is_event(event).map(|_| {
let notifacion: PopupNotificationRef = self.ui.popup_notification(id!(notification));
notifacion.close(cx);
@ -330,6 +389,4 @@ impl AppMain for App {
}
}
impl App {}
app_main!(App);

View File

@ -6,9 +6,9 @@ live_design! {
import crate::widgets::renderer::IRenderer;
import makepad_draw::shader::std::*;
import crate::widgets::selector_modal::*;
import crate::widgets::background_text::BackgroundLabel;
import crate::widgets::selector::*;
import crate::sidepanel::*;
HELLO = "Hello, World!";
@ -63,14 +63,7 @@ live_design! {
y: 0.5
},
<View> {
width: 600
height: 500
align: {
x: 0.5,
y: 0.5
}
}
quad = <Area> {}
}
@ -83,108 +76,10 @@ live_design! {
Controller = <RectView> {
draw_bg: { color: (THEME_COLOR_U_1) }
height: Fill, width: Fill
padding: 10.,
// padding: 10.,
flow: Down,
spacing: 15.,
scroll_bars: <ScrollBars> {show_scroll_x: false, show_scroll_y: true}
<H4>{ text: "Controller"}
start = <Button> {
text: "Start"
width: Fill
}
<Group> {
<Slider> {
width: Fill,
height: 30,
draw_slider:{
slider_type: Horizontal
},
}
<Slider> {
width: Fill,
height: 30,
draw_slider:{
slider_type: Horizontal
},
}
<Slider> {
width: Fill,
height: 30,
draw_slider:{
slider_type: Horizontal
},
}
open_modal = <Button> {
text: "Click me"
width: Fill
}
<CheckBox> {text: "Check me"}
<DropDown>{
height: 24,
width: Fill,
labels: ["ValueOne", "ValueTwo","Thrice","FourthValue","OptionE","Hexagons"],
values: [ ValueOne,ValueTwo,Thrice,FourthValue,OptionE,Hexagons]
}
<Slider> {
width: Fill,
height: 30,
draw_slider:{
slider_type: Horizontal
},
}
}
<H4>{ text: "Input"}
<View>{
flow: Right
width: Fill
height: Fit
<BackgroundLabel>{
text: "Holy Shit",
}
<BackgroundLabel>{
text: "Holy Shit",
}
<BackgroundLabel>{
text: "Holy Shit",
}
}
<Group> {
<ButtonGroup> {
spacing: 10.,
<Button> {text: "One"}
<Button> {text: "Two"}
<Button> {text: "Three"}
}
<TextInput> {
width: Fill,
height: Fit,
text: "Text Input"
}
<TextFlowBase> {
width: Fill,
height: Fit,
<Text> {text: "Text Flow"}
}
}
<SidePanel> {}
}
}

View File

@ -1,14 +1,13 @@
use crate::{
app::AppAction,
errors::AppErr,
widgets::selector::{ItemKey, ItemValue, SelectorWidgetRefExt},
selector::SelectorBaseWidgetRefExt,
widgets::selector::{ItemKey, ItemValue},
GIAPP, RUNTIME,
};
use ::log::info;
use makepad_widgets::*;
use std::path::{Path, PathBuf};
use makepad_widgets::{id, Cx, ToUIReceiver, ToUISender, WidgetRef};
use makepad_widgets::{id, Cx, ToUIReceiver, WidgetRef};
use mp_core::{datapool::Value, errors::DataError, Data};
use crate::DATAPOOL;
@ -45,17 +44,16 @@ impl FileManager {
}
pub fn try_deal_file(&mut self, ui: &WidgetRef, cx: &mut Cx) {
let selector = ui.selector(id!(selector));
// let selector = ui.selector(id!(selector));
let selector = ui.selector_base(id!(selector));
let selector_modal = ui.modal(id!(selector_modal));
let loading_modal = ui.modal(id!(loading_modal));
while let Ok(mut data) = self.receiver.try_recv() {
if data.len() == 1 {
let (path, data) = data.pop().unwrap();
if let Ok(data) = data {
info!("Data: {:?}", data);
cx.action(AppAction::Notification {
message: format!("hello,World, {:?}", data),
duration: 3.0,
});
let mut items = vec![];
@ -64,6 +62,7 @@ impl FileManager {
let supported_elements = pipelines.supported_elements(&data);
let key_name = ItemKey {
data_idx: *idx,
path: "".to_string(),
category: data.description.clone(),
description: data.description.clone(),
@ -71,9 +70,14 @@ impl FileManager {
let item_values = supported_elements
.iter()
.map(|v| ItemValue {
.map(|v| {
(
ItemValue {
name: v.name().to_string(),
icon: "".to_string(),
},
v.clone(),
)
})
.collect::<Vec<_>>();
@ -85,12 +89,13 @@ impl FileManager {
.map(|v| v.to_string_lossy())
.unwrap_or_default();
selector.set_items(cx, items);
selector.set_base_name(&base_name);
selector.set_items(cx, data, &base_name, items);
selector_modal.open(cx);
}
} else {
}
}
loading_modal.close(cx);
}
}

View File

@ -9,7 +9,9 @@ pub mod app_ui;
pub mod errors;
pub mod file;
pub mod menu;
pub mod render_task;
pub mod selector;
pub mod sidepanel;
pub mod task_register;
pub mod widgets;
use mp_core::{config::Setting, datapool::DataPool, plugin_system::PluginManager};

View File

@ -16,16 +16,16 @@ pub fn handle_menu(cx: &mut Cx, event: &Event) {
}
}
pub fn spawn_background(app: &mut App, event: &Event) {
match event {
Event::Draw(_) => {
// Render Task
let tasks = app.render_tasks.clone();
RUNTIME.spawn(async move {
let tasks = tasks.lock().await;
tasks.render().await;
});
}
_ => {}
}
}
// pub fn spawn_background(app: &mut App, event: &Event) {
// match event {
// Event::Draw(_) => {
// // Render Task
// let tasks = app.render_tasks.clone();
// RUNTIME.spawn(async move {
// let tasks = tasks.lock().await;
// tasks.render().await;
// });
// }
// _ => {}
// }
// }

View File

@ -1,93 +0,0 @@
use crate::{
widgets::renderer,
windows_manager::{AllBuffers, WindowsManager},
GIAPP, RUNTIME,
};
use element_bridge::TextureBridge;
use futures::future::BoxFuture;
use log::info;
use makepad_widgets::{Cx, Texture, ToUISender};
use mp_elements::{
app::{DrawList, RenderWindow},
App,
};
use std::sync::{Arc, Mutex};
#[derive(Default)]
pub struct RenderTasks {
pub(crate) tasks: Vec<RenderTask>,
}
impl RenderTasks {
pub fn new() -> Self {
Self { tasks: Vec::new() }
}
fn push_task(&mut self, task: RenderTask) {
self.tasks.push(task);
}
pub fn register_task(
&mut self,
sender: ToUISender<Vec<u8>>,
bridge_id: usize,
bridge: Arc<RenderWindow>,
draw_list: DrawList,
) {
let task = RenderTask::new(bridge_id, sender, bridge, draw_list);
self.push_task(task);
}
pub fn clear(&mut self) {
self.tasks.clear();
}
pub async fn render(&self) {
// Draw all tasks
let futures: Vec<_> = self
.tasks
.iter()
.map(|task| async {
info!("Drawing task");
task.draw().await;
})
.collect();
futures::future::join_all(futures).await;
}
}
pub struct RenderTask {
bridge_id: usize,
sender: ToUISender<Vec<u8>>,
buffer: Arc<Mutex<Vec<u8>>>,
render_window: Arc<RenderWindow>,
draw_list: DrawList,
}
impl RenderTask {
pub fn new(
bridge_id: usize,
sender: ToUISender<Vec<u8>>,
bridge_render_window: Arc<RenderWindow>,
draw_list: DrawList,
) -> Self {
Self {
bridge_id,
sender,
buffer: Arc::new(Mutex::new(vec![0u8; 256 * 256 * 4])),
render_window: bridge_render_window,
draw_list,
}
}
pub async fn draw(&self) {
GIAPP
.draw(&self.render_window, self.draw_list.clone())
.await;
let ctx = GIAPP.ctx();
let mut bf = Vec::with_capacity(256 * 256 * 4);
TextureBridge::load_data(&mut bf, ctx, &self.render_window).await;
self.sender.send(bf).unwrap();
}
}

146
mp/src/selector.rs Normal file
View File

@ -0,0 +1,146 @@
use std::sync::Arc;
use crate::{
task_register::TaskConfig,
widgets::selector::{ItemKey, ItemValue, SelectorAction, SelectorWidgetRefExt},
CONFIG,
};
use ::log::info;
use makepad_widgets::*;
use mp_core::{Data, DataValue};
use mp_elements::elements::Elements;
live_design! {
import crate::widgets::selector::Selector;
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
SelectorBase = {{SelectorBase}} {
<View> {
height: Fill
width: Fill
show_bg: false
selector = <Selector> {}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct SelectorBase {
#[deref]
view: View,
#[rust]
data: Option<DataValue>,
}
#[derive(Debug, Clone, DefaultNone)]
pub enum SelectorModalAction {
RegisterTask {
elements: Vec<Elements>,
data: Arc<Data>,
config: Vec<Option<TaskConfig>>,
},
Close,
None,
}
impl WidgetMatchEvent for SelectorBase {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
let modal_dismissed = actions
.iter()
.find(|a| matches!(a.downcast_ref(), Some(ModalAction::Dismissed)))
.is_some();
for action in actions {
match action.as_widget_action().cast() {
SelectorAction::Selected { element, key, item } => {
info!("Selected: {:?} {:?}", key, item);
log!("Registering Task ... ... ");
let data_idx = key.data_idx;
// Register the task
let data = self.data.as_ref().unwrap().get(data_idx).unwrap().clone();
let mut configs = Vec::new();
for ele in element.iter() {
match ele.init_config(&data, &CONFIG) {
Ok(config) => configs.push(Some(config)),
Err(e) => {
error!(
"Failed to init config for element: {:?}, cause of: {:?}",
ele, e
);
return;
}
}
}
cx.widget_action(
self.widget_uid(),
&scope.path,
SelectorModalAction::RegisterTask {
elements: element,
data: data,
config: configs,
},
);
// Close the modal
if !modal_dismissed {
cx.widget_action(
self.widget_uid(),
&scope.path,
SelectorModalAction::Close,
);
}
}
_ => {}
}
}
}
}
impl Widget for SelectorBase {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}
impl SelectorBase {
pub fn set_items(&mut self, cx: &mut Cx, items: Vec<(ItemKey, Vec<(ItemValue, Elements)>)>) {
let selector = self.view.widget(id!(selector)).as_selector();
selector.set_items(cx, items);
}
pub fn set_data(&mut self, data: DataValue) {
self.data = Some(data);
}
pub fn set_base_name(&mut self, name: &str) {
let selector = self.view.widget(id!(selector)).as_selector();
selector.set_base_name(name);
}
}
impl SelectorBaseRef {
pub fn set_items(
&self,
cx: &mut Cx,
data: DataValue,
base_name: &str,
items: Vec<(ItemKey, Vec<(ItemValue, Elements)>)>,
) {
if let Some(mut s) = self.borrow_mut() {
s.set_data(data);
s.set_items(cx, items);
s.set_base_name(base_name);
}
}
}

131
mp/src/sidepanel.rs Normal file
View File

@ -0,0 +1,131 @@
use makepad_widgets::*;
live_design! {
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import crate::widgets::element_panels::*;
SidePanel = {{SidePanel}}{
<View> {
height: Fill
width: Fill
scroll_bars: <ScrollBars> {show_scroll_x: false, show_scroll_y: true}
flow: Down
start = <Button> {
text: "Start"
width: Fill
height: 30
margin: {top: 30, bottom: 30}
}
panel_dock = <Dock> {
width: Fill
height: Fill
root = Splitter {
axis: Vertical,
align: FromA(250.0),
a: common_tab,
b: element_tabs
}
common_tab = Tab {
name: "Common"
kind: common
}
common = <View> {
height: Fill
width: Fill
draw_bg: {
color: #f
}
}
element_tabs = Tabs {
tabs: [new_tab]
}
ppi_tab = Tab {
name: "PPI"
kind: ppi
}
new_tab = Tab {
name: "New"
kind: new
}
ppi = <PPIPanel> {}
new_element = <View> {
height: Fill
width: Fill
draw_bg: {
color: #f
}
}
new = <View> {
height: Fill
width: Fill
align: {x:0.5, y:0.5}
new_tab_button = <Button> {
text: "New"
width: 100
height: 100
}
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct SidePanel {
#[deref]
view: View,
}
impl Widget for SidePanel {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
let dock = self.dock(id!(panel_dock));
self.match_event(cx, event);
}
}
impl MatchEvent for SidePanel {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
let new_tab_button = self.button(id!(new_tab_button));
let dock = self.dock(id!(panel_dock));
if new_tab_button.clicked(actions) {
let (tab_bar, pos) = dock.find_tab_bar_of_tab(live_id!(new_tab)).unwrap();
let result = dock.create_and_select_tab(
cx,
tab_bar,
live_id!(ppi),
live_id!(new_element),
"PPI".into(),
live_id!(CloseableTab),
None,
);
dock.close_tab(cx, live_id!(new_tab));
}
}
}

246
mp/src/task_register.rs Normal file
View File

@ -0,0 +1,246 @@
use futures::future::BoxFuture;
use log::info;
use makepad_widgets::ToUISender;
use std::{
any::Any,
collections::HashMap,
sync::{Arc, Mutex},
};
use tokio::sync::mpsc::Sender;
use crate::{errors::AppErr, windows_manager::WindowId, GIAPP, RUNTIME};
use element_bridge::{TextureBridge, Window};
use mp_core::Data;
use mp_elements::{
app::{Ctx, DataBufferPool, DrawList, RenderWindow},
elements::Elements,
};
use std::{hash::Hash, sync::atomic::AtomicU64};
static TASK_ID: AtomicU64 = AtomicU64::new(0);
pub type TaskConfig = Arc<Mutex<dyn Any + Send + 'static>>;
type TaskList = Vec<BoxFuture<'static, ()>>;
#[derive(Default)]
pub struct TaskManager {
req: Option<Sender<TaskList>>,
render_tasks: Vec<Arc<RenderTask>>,
sender: Option<ToUISender<(TaskId, Vec<f32>)>>,
}
impl TaskManager {
pub fn new() -> Self {
Self {
req: None,
render_tasks: Vec::new(),
sender: None,
}
}
pub fn supported_elements(&self, data: &Data) -> Vec<Elements> {
GIAPP.pipelines().supported_elements(data)
}
fn ctx(&self) -> &Ctx {
GIAPP.ctx()
}
fn buffer_pool(&self) -> &DataBufferPool {
GIAPP.buffer_pool()
}
pub fn set_sender(&mut self, sender: ToUISender<(TaskId, Vec<f32>)>) {
self.sender = Some(sender);
}
pub fn get_draw_list(
&self,
data: &Data,
element: Vec<Elements>,
configs: &Vec<Option<TaskConfig>>,
) -> Result<DrawList, AppErr> {
let ctx = self.ctx();
let buffer_pool = self.buffer_pool();
let mut draw_list = DrawList::new();
for (element, config) in element.into_iter().zip(configs.iter()) {
let attachment = if let Some(config) = config {
let config = config.lock().unwrap();
let config = &*config;
element.load_data(ctx, data, buffer_pool, Some(config))?
} else {
element.load_data(ctx, data, buffer_pool, None)?
};
draw_list.push(element, Arc::new(attachment));
}
Ok(draw_list)
}
pub fn redraw(&self) {
let sender = self.req.clone().unwrap();
let mut tasks = Vec::new();
for t in self.render_tasks.iter() {
let sender = self.sender.clone().unwrap();
let t = t.clone();
tasks.push(Box::pin(async move {
let buffer = t.buffer.clone();
let buffer = t.draw().await;
sender.send((t.id.clone(), buffer)).unwrap();
}) as BoxFuture<'static, ()>);
}
RUNTIME.spawn(async move {
sender.send(tasks).await.unwrap();
});
}
fn push_task(&mut self, task: RenderTask) {
self.render_tasks.push(Arc::new(task));
}
pub fn register_task(
&mut self,
render_window: Arc<RenderWindow>,
draw_list: DrawList,
config: Vec<Option<TaskConfig>>,
) -> TaskId {
let configs = {
let mut configs = HashMap::new();
for (idx, conf) in config.into_iter().enumerate() {
if let Some(p) = conf {
configs.insert(idx, p);
}
}
configs
};
let task = RenderTask::new(draw_list, render_window, configs);
let task_id = task.id.clone();
self.push_task(task);
task_id
}
pub fn register_task_for_drawlist(
&mut self,
draw_list: DrawList,
render_window: Arc<RenderWindow>,
config: Vec<Option<TaskConfig>>,
) -> Result<TaskId, AppErr> {
let id = self.register_task(render_window, draw_list, config);
Ok(id)
}
pub fn run(&mut self) {
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
self.req = Some(tx);
RUNTIME.spawn(async move {
while let Some(tasks) = rx.recv().await {
futures::future::join_all(tasks).await;
}
});
}
}
#[derive(Default)]
pub struct BridgeManager {
inner: HashMap<WindowId, TextureBridge>,
net: HashMap<TaskId, Vec<WindowId>>,
}
impl BridgeManager {
pub fn new() -> Self {
Self {
inner: HashMap::new(),
net: HashMap::new(),
}
}
pub fn get_bridge(&self, id: &WindowId) -> Option<&TextureBridge> {
self.inner.get(id)
}
pub fn get_bridge_mut(&mut self, id: &WindowId) -> Option<&mut TextureBridge> {
self.inner.get_mut(&id)
}
pub fn new_bridge(&mut self, id: WindowId) {
let bridge = TextureBridge::new(
&GIAPP,
// Default window size, MAX size
Window {
width: 1920,
height: 1080,
},
);
self.inner.insert(id, bridge);
}
pub fn create_net(&mut self, task_id: TaskId, window_id: WindowId) {
self.net
.entry(task_id)
.or_insert(Vec::new())
.push(window_id);
}
pub fn get_net(&self, task_id: &TaskId) -> Option<&Vec<WindowId>> {
self.net.get(task_id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TaskId(u64);
impl TaskId {
fn new() -> Self {
Self(TASK_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst))
}
}
pub struct RenderTask {
pub id: TaskId,
buffer: Arc<Mutex<Vec<u8>>>,
render_window: Arc<RenderWindow>,
configs: HashMap<usize, TaskConfig>,
draw_list: DrawList,
}
impl RenderTask {
pub fn new(
draw_list: DrawList,
render_window: Arc<RenderWindow>,
config: HashMap<usize, Arc<Mutex<dyn Any + Send + 'static>>>,
) -> Self {
Self {
id: TaskId::new(),
buffer: Arc::new(Mutex::new(vec![0u8; 1920 * 1080 * 4])),
configs: config,
render_window,
draw_list,
}
}
pub async fn draw(&self) -> Vec<f32> {
GIAPP
.draw(&self.render_window, self.draw_list.clone())
.await;
let ctx = GIAPP.ctx();
// let buffer = self.buffer.clone();
let data = TextureBridge::load_data(ctx, &self.render_window).await;
data
}
}
#[derive(Default)]
pub struct ConfigManager {
configs: HashMap<TaskId, Vec<Option<TaskConfig>>>,
}
impl ConfigManager {
pub fn register_config(&mut self, task_id: TaskId, config: Vec<Option<TaskConfig>>) {
self.configs.insert(task_id, config);
}
}

View File

@ -1,31 +1,60 @@
use std::cell::RefCell;
use std::sync::Arc;
use std::sync::Mutex;
use crate::windows_manager::WindowId;
use crate::windows_manager::WM;
use crate::GIAPP;
use ::log::info;
use element_bridge::{TextureBridge, Window};
use element_bridge::TextureBridge;
use makepad_widgets::makepad_derive_widget::*;
use makepad_widgets::makepad_draw::*;
use makepad_widgets::widget::*;
use makepad_widgets::Image;
use makepad_widgets::ImageWidgetRefExt;
use makepad_widgets::View;
use mp_elements::renderer::camera::Camera;
use mp_elements::renderer::projection::Projection;
live_design! {
Area = {{TArea}} {}
Area = {{TArea}} {
import makepad_draw::shader::std::*;
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
<View> {
height: Fill
width: Fill
video_frame = <Image> {
height: Fill
width: Fill
fit: Biggest
draw_bg: {
uniform image_size: vec2
fn get_pixel(self, pos:vec2) -> vec4 {
let pix = self.pos * self.image_size;
let data = sample2d(self.image, pix).xyzw;
return vec4(data.x, data.y, data.z, 1,0);
}
fn pixel(self) -> vec4 {
// return self.get_pixel(self.pos);
return vec4(1.0, 1.0, 1.0, 1.0);
}
}
}
}
}
}
#[derive(Live, Widget)]
pub struct TArea {
#[redraw]
#[deref]
#[live]
draw: DrawQuad,
#[walk]
walk: Walk,
#[layout]
layout: Layout,
view: View,
#[live]
id: String,
#[live]
@ -36,9 +65,16 @@ pub struct TArea {
width_scale: f64,
#[rust]
bridge: Option<Arc<Mutex<TextureBridge>>>,
window_id: Option<WindowId>,
#[rust]
windows: WM,
texture: Option<Texture>,
}
#[derive(Clone, DefaultNone, Debug)]
pub enum TAreaAction {
GetFocus(WindowId),
None,
}
#[derive(Debug, Clone)]
@ -81,25 +117,6 @@ impl TAreaState {
pub fn resize(&mut self, width: f32, height: f32) {
self.projection.resize(width as u32, height as u32);
}
pub fn set_bridge(&mut self, bridge: TextureBridge) {
self.bridge = Some(bridge);
}
pub fn new_bridge(&mut self, cx: &mut Cx, window: Window) {
let texture = Texture::new_with_format(
cx,
TextureFormat::VecRGBAf32 {
width: window.width as usize,
height: window.height as usize,
data: None,
updated: TextureUpdated::Full,
},
);
let bridge = TextureBridge::new(texture, &GIAPP, window);
self.bridge = Some(bridge);
}
}
impl LiveHook for TArea {}
@ -110,68 +127,73 @@ pub enum MyWidgetAction {
}
impl Widget for TArea {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {}
fn draw_walk(&mut self, cx: &mut Cx2d, _scope: &mut Scope, walk: Walk) -> DrawStep {
let rect = cx.peek_walk_turtle(walk);
let current_size = rect.size;
let current_texture = self.get_texture();
// If the texture is not created or the size is different, recreate the texture
if current_texture.is_none()
|| current_texture
.unwrap()
.get_format(cx)
.vec_width_height()
.map(|(w, h)| (w as f64, h as f64))
!= Some((current_size.x, current_size.y))
{
let window_id = WindowId::new(&self.id);
let (width, height) = (current_size.x, current_size.y);
let dpi = cx.current_dpi_factor();
let window = Window {
height: height as u32,
width: width as u32,
};
info!("Creating texture for window: {:?}", window_id);
let texture = Texture::new_with_format(
cx,
TextureFormat::VecRGBAf32 {
width: window.width as usize,
height: window.height as usize,
data: None,
updated: TextureUpdated::Full,
},
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
match event.hits(cx, self.view.area()) {
Hit::FingerDown(_) => {
cx.widget_action(
self.widget_uid(),
&scope.path,
TAreaAction::GetFocus(self.window_id.clone().unwrap()),
);
let bridge = TextureBridge::new(texture, &GIAPP, window);
let mut windows = self.windows.wm();
let (_, bridge) = windows.add_window(window_id, bridge);
self.bridge = Some(bridge);
}
self.draw.draw_walk(cx, walk);
DrawStep::done()
_ => {}
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}
impl TArea {
fn get_texture(&self) -> Option<Texture> {
self.bridge
.as_ref()
.map(|bridge| bridge.lock().unwrap().texture().clone())
fn set_id(&mut self, id: WindowId) {
self.window_id = Some(id);
}
fn set_windows(&mut self, windows: WM) {
info!("Setting windows for area: {}", self.id);
self.windows = windows;
fn set_texture(&mut self, cx: &mut Cx, texture: Texture) {
self.texture = Some(texture);
self.view
.widget(id!(video_frame))
.as_image()
.set_texture(cx, self.texture.clone());
self.view.widget(id!(video_frame)).as_image().set_uniform(
cx,
id!(image_size),
&[1920.0, 1080.0],
);
}
fn get_texutre(&self) -> Option<Texture> {
self.texture.clone()
}
}
impl TAreaRef {
pub fn set_windows(&self, windows: WM) {
if let Some(mut area) = self.borrow_mut() {
area.set_windows(windows);
pub fn set_id(&self, id: WindowId) {
if let Some(mut s) = self.borrow_mut() {
s.set_id(id);
}
}
pub fn set_texture(&self, cx: &mut Cx, texture: Texture) {
if let Some(mut s) = self.borrow_mut() {
s.set_texture(cx, texture);
}
}
pub fn create_texture(&self, cx: &mut Cx) {
if let Some(mut s) = self.borrow_mut() {
let texture = Texture::new(cx);
s.set_texture(cx, texture);
}
}
pub fn get_texture(&self) -> Option<Texture> {
if let Some(s) = self.borrow() {
s.get_texutre()
} else {
None
}
}
}

View File

@ -0,0 +1,103 @@
use std::sync::{Arc, Mutex};
use makepad_widgets::*;
use mp_elements::elements::ppi::PPIConfig;
live_design! {
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
Group = <RoundedView> {
flow: Down
spacing: 10.
padding: 15.
height: Fit
width: Fill
draw_bg: {
instance border_width: 1.0
instance border_color: (THEME_COLOR_U_2)
}
}
PPIPanel = {{PPIPanel}}{
<View> {
height: Fill
width: Fill
flow: Down
padding: {left:10, right: 10}
spacing: 10
<H4> {
text: "PPI Control"
}
<Group> {
<Button> {
width: Fill
text: "Add"
}
layer = <Slider> {
width: Fill
min: 0
max:1
step:0.01
text: "Scale"
}
<Slider> {
width: Fill
min: 0
max: 50
step: 1
text: "Size"
}
threed_switch = <CheckBox> {
text: "3d"
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct PPIPanel {
#[deref]
view: View,
#[rust]
config: Arc<Mutex<PPIConfig>>,
}
impl Widget for PPIPanel {
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
self.match_event(cx, event);
}
}
impl MatchEvent for PPIPanel {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions) {
let threed = self.check_box(id!(threed_switch));
let mut config = self.config.lock().unwrap();
config.three_d = threed.selected(cx);
let layer = self.slider(id!(layer));
// config.layer = layer.value();
if layer.value() != Some(config.layer as f64) {
config.layer = layer.value().unwrap() as f32;
}
}
}

264
mp/src/widgets/loading.rs Normal file
View File

@ -0,0 +1,264 @@
use makepad_widgets::*;
live_design! {
import makepad_widgets::base::*;
import makepad_widgets::theme_desktop_dark::*;
import makepad_draw::shader::std::*;
LoadingModal = {{LoadingModal}} {
width: Fit
height: Fit
<RoundedView> {
flow: Down
width: 600
height: Fit
padding: {top: 25, right: 30 bottom: 30 left: 45}
spacing: 10
show_bg: true
draw_bg: {
color: #fff
radius: 3.0
}
title_view = <View> {
width: Fill,
height: Fit,
flow: Right
padding: {top: 0, bottom: 40}
align: {x: 0.5, y: 0.0}
title = <Label> {
text: "Loading content..."
draw_text: {
text_style: <TITLE_TEXT>{font_size: 13},
color: #000
}
}
}
body = <View> {
width: Fill,
height: Fit,
flow: Down,
spacing: 40,
status = <Label> {
width: Fill
draw_text: {
text_style: <REGULAR_TEXT>{
font_size: 11.5,
height_factor: 1.3
},
color: #000
wrap: Word
}
}
<View> {
width: Fill, height: Fit
flow: Right,
align: {x: 1.0, y: 0.5}
spacing: 20
// cancel_button = <RobrixIconButton> {
// padding: {left: 15, right: 15}
// // draw_icon: {
// // svg_file: (ICON_BLOCK_USER)
// // color: (COLOR_DANGER_RED),
// // }
// icon_walk: {width: 0, height: 0 }
// draw_bg: {
// border_color: (COLOR_DANGER_RED),
// color: #fff0f0 // light red
// }
// text: "Cancel"
// draw_text:{
// color: (COLOR_DANGER_RED),
// }
// }
}
}
}
}
}
#[derive(Live, LiveHook, Widget)]
pub struct LoadingModal {
#[deref]
view: View,
#[rust]
state: LoadingModalState,
}
impl Drop for LoadingModal {
fn drop(&mut self) {
if let LoadingModalState::BackwardsPaginateUntilEvent {
// target_event_id,
// request_sender,
..
} = &self.state
{
warning!(
"Dropping LoadingModal with target_event_id ",
// target_event_id
);
// request_sender.send_if_modified(|requests| {
// let initial_len = requests.len();
// requests.retain(|r| &r.target_event_id != target_event_id);
// // if we actually cancelled this request, notify the receivers
// // such that they can stop looking for the target event.
// requests.len() != initial_len
// });
}
}
}
/// An action sent from this LoadingModal widget to its parent widget,
/// which is currently required in order for the parent to close this modal.
#[derive(Clone, Debug, DefaultNone)]
pub enum LoadingModalAction {
Close,
None,
}
/// The state of a LoadingModal: the possible tasks that it may be performing.
#[derive(Clone, DefaultNone)]
pub enum LoadingModalState {
/// The room is being backwards paginated until the target event is reached.
BackwardsPaginateUntilEvent,
/// The loading modal is displaying an error message until the user closes it.
Error(String),
/// The LoadingModal is not doing anything and can be hidden.
None,
}
impl Widget for LoadingModal {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
self.view.handle_event(cx, event, scope);
self.widget_match_event(cx, event, scope);
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
self.view.draw_walk(cx, scope, walk)
}
}
impl WidgetMatchEvent for LoadingModal {
fn handle_actions(&mut self, cx: &mut Cx, actions: &Actions, scope: &mut Scope) {
let widget_uid = self.widget_uid();
// let cancel_button = self.button(id!(cancel_button));
let modal_dismissed = actions
.iter()
.find(|a| matches!(a.downcast_ref(), Some(ModalAction::Dismissed)))
.is_some();
if modal_dismissed {
log!(
"LoadingModal: close requested: {}",
if modal_dismissed {
"by modal dismiss"
} else {
"by cancel button"
}
);
if let LoadingModalState::BackwardsPaginateUntilEvent {
// target_event_id,
// request_sender,
..
} = &self.state
{
// let _did_send = request_sender.send_if_modified(|requests| {
// let initial_len = requests.len();
// requests.retain(|r| &r.target_event_id != target_event_id);
// // if we actually cancelled this request, notify the receivers
// // such that they can stop looking for the target event.
// requests.len() != initial_len
// });
// log!(
// "LoadingModal: {} cancel request for target_event_id: {target_event_id}",
// if _did_send { "Sent" } else { "Did not send" },
// );
}
self.set_state(cx, LoadingModalState::None);
// If the modal was dismissed by clicking outside of it, we MUST NOT emit
// a `LoadingModalAction::Close` action, as that would cause
// an infinite action feedback loop.
if !modal_dismissed {
cx.widget_action(widget_uid, &scope.path, LoadingModalAction::Close);
}
}
}
}
impl LoadingModal {
pub fn set_state(&mut self, cx: &mut Cx, state: LoadingModalState) {
// let cancel_button = self.button(id!(cancel_button));
match &state {
LoadingModalState::BackwardsPaginateUntilEvent {
// target_event_id,
// events_paginated,
..
} => {
self.set_title(cx, "Searching older messages...");
self.set_status(
cx,
&format!(
"Looking for event \n\n\
",
),
);
// cancel_button.set_text_and_redraw(cx, "Cancel");
}
LoadingModalState::Error(error_message) => {
self.set_title(cx, "Error loading content");
self.set_status(cx, error_message);
// cancel_button.set_text_and_redraw(cx, "Okay");
}
LoadingModalState::None => {}
}
self.state = state;
self.redraw(cx);
}
pub fn set_status(&mut self, cx: &mut Cx, status: &str) {
self.label(id!(status)).set_text_and_redraw(cx, status);
}
pub fn set_title(&mut self, cx: &mut Cx, title: &str) {
self.label(id!(title)).set_text_and_redraw(cx, title);
}
}
impl LoadingModalRef {
pub fn take_state(&self) -> LoadingModalState {
self.borrow_mut()
.map(|mut inner| std::mem::take(&mut inner.state))
.unwrap_or(LoadingModalState::None)
}
pub fn set_state(&self, cx: &mut Cx, state: LoadingModalState) {
let Some(mut inner) = self.borrow_mut() else {
return;
};
inner.set_state(cx, state);
}
pub fn set_status(&self, cx: &mut Cx, status: &str) {
let Some(mut inner) = self.borrow_mut() else {
return;
};
inner.set_status(cx, status);
}
pub fn set_title(&self, cx: &mut Cx, title: &str) {
let Some(mut inner) = self.borrow_mut() else {
return;
};
inner.set_title(cx, title);
}
}

View File

@ -1,4 +1,6 @@
pub mod area;
pub mod background_text;
pub mod element_panels;
pub mod loading;
pub mod renderer;
pub mod selector;

View File

@ -76,25 +76,6 @@ impl RenderState {
pub fn resize(&mut self, width: f32, height: f32) {
self.projection.resize(width as u32, height as u32);
}
pub fn set_bridge(&mut self, bridge: TextureBridge) {
self.bridge = Some(bridge);
}
pub fn new_bridge(&mut self, cx: &mut Cx, window: Window) {
let texture = Texture::new_with_format(
cx,
TextureFormat::VecRGBAf32 {
width: window.width as usize,
height: window.height as usize,
data: None,
updated: TextureUpdated::Full,
},
);
let bridge = TextureBridge::new(texture, &GIAPP, window);
self.bridge = Some(bridge);
}
}
impl LiveHook for Renderer {}

View File

@ -2,6 +2,8 @@ use std::collections::HashMap;
use indexmap::IndexSet;
use makepad_widgets::*;
use mp_core::Data;
use mp_elements::elements::{Element, Elements};
live_design! {
import makepad_widgets::base::*;
@ -276,12 +278,16 @@ pub enum SelectorAction {
None,
SelectItem(ItemKey, ItemValue, usize, usize),
Selected(ItemKey, usize),
Unselected(ItemKey, usize),
Selected {
element: Vec<Elements>,
key: ItemKey,
item: ItemValue,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ItemKey {
pub data_idx: usize,
pub path: String,
pub category: String,
pub description: String,
@ -545,7 +551,7 @@ pub struct Selector {
view: View,
#[rust]
class: Vec<(ItemKey, Vec<ItemValue>)>,
class: Vec<(ItemKey, Vec<(ItemValue, Elements)>)>,
#[rust]
selected: Option<(ItemKey, ItemValue, usize, usize)>,
@ -555,7 +561,9 @@ impl Widget for Selector {
fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope) {
let list = self.view.widget(id!(list)).as_portal_list();
let ok_button = self.view.widget(id!(ok_button)).as_button();
for action in cx.capture_actions(|cx| self.view.handle_event(cx, event, scope)) {
let actions = cx.capture_actions(|cx| self.view.handle_event(cx, event, scope));
for action in actions.iter() {
match action.as_widget_action().cast() {
SelectorAction::SelectItem(key, item, list_id, item_id) => {
let raw_selected = &self.selected;
@ -570,13 +578,25 @@ impl Widget for Selector {
list.get_item(list_id).map(|v| {
v.1.as_selector_list().set_selected(cx, item_id, true);
});
ok_button.set_enabled(self.selected.is_some());
}
_ => {}
}
}
if ok_button.clicked(&actions) {
let selected = self.selected.clone().unwrap();
let elements = self.class[selected.2 / 2].1[selected.3].1.clone();
cx.widget_action(
self.widget_uid(),
&scope.path,
SelectorAction::Selected {
element: vec![elements],
key: selected.0,
item: selected.1,
},
);
}
}
fn draw_walk(&mut self, cx: &mut Cx2d, scope: &mut Scope, walk: Walk) -> DrawStep {
@ -586,6 +606,10 @@ impl Widget for Selector {
continue;
};
let ok_button = self.view.widget(id!(ok_button)).as_button();
ok_button.set_enabled(self.selected.is_some());
let count = self.class.len() * 2;
// TitleBar and List
list.set_item_range(cx, 0, count);
@ -609,7 +633,7 @@ impl Widget for Selector {
let list_item = list.item(cx, item_id, live_id!(list_item));
let list = list_item.as_selector_list();
for item in prepared.1.iter() {
for (item, _) in prepared.1.iter() {
list.insert_if_not_exists(cx, item_id, item);
}
@ -626,19 +650,23 @@ impl Widget for Selector {
}
impl Selector {
fn set_items(&mut self, cx: &mut Cx, items: Vec<(ItemKey, Vec<ItemValue>)>) {
fn set_items(&mut self, cx: &mut Cx, items: Vec<(ItemKey, Vec<(ItemValue, Elements)>)>) {
self.class = items;
let list = self.view.widget(id!(list)).as_portal_list();
list.clear_query_cache();
self.view.redraw(cx);
}
fn set_base_name(&mut self, name: &str) {
let title_bar = self.view.widget(id!(file_base_name)).as_label();
title_bar.set_text(name);
title_bar.set_text(format!("File: {}", name).as_str());
}
}
impl SelectorRef {
pub fn set_items(&self, cx: &mut Cx, items: Vec<(ItemKey, Vec<ItemValue>)>) {
pub fn set_items(&self, cx: &mut Cx, items: Vec<(ItemKey, Vec<(ItemValue, Elements)>)>) {
if let Some(mut item) = self.borrow_mut() {
item.set_items(cx, items);
}

View File

@ -1,103 +1,97 @@
use std::{
collections::HashMap,
sync::{Arc, Mutex, MutexGuard},
};
use makepad_widgets::{Cx, PointUsize, RectUsize, SizeUsize, Texture, ToUIReceiver, ToUISender};
use element_bridge::TextureBridge;
use crate::task_register::{BridgeManager, TaskId};
use crate::widgets::area::TAreaRef;
use std::collections::HashMap;
use std::sync::atomic::AtomicU64;
pub type AllBuffers = Arc<Mutex<HashMap<usize, Arc<tokio::sync::Mutex<Vec<u8>>>>>>;
static WINDOW_ID: AtomicU64 = AtomicU64::new(0);
#[derive(Eq, Hash, PartialEq, Debug)]
#[derive(Eq, Hash, PartialEq, Debug, Clone)]
pub struct WindowId {
id: u64,
name: String,
}
#[derive(Default)]
pub struct WindowsManager {
inner: HashMap<WindowId, Arc<Mutex<TextureBridge>>>,
all_bridges: Vec<(usize, Arc<Mutex<TextureBridge>>)>,
all_revelant_buffers: Arc<Mutex<HashMap<usize, Arc<tokio::sync::Mutex<Vec<u8>>>>>>,
inner: HashMap<WindowId, TAreaRef>,
recv: ToUIReceiver<(TaskId, Vec<f32>)>,
}
impl WindowsManager {
pub fn new() -> Self {
let recv = ToUIReceiver::default();
Self {
inner: HashMap::new(),
all_bridges: Vec::new(),
all_revelant_buffers: Arc::new(Mutex::new(HashMap::new())),
recv,
}
}
pub fn edit_window<F>(&mut self, id: WindowId, f: F) -> (usize, Arc<Mutex<TextureBridge>>)
where
F: FnOnce(&mut TextureBridge),
{
let bridge = self.inner.get(&id).unwrap();
let mut _bridge = bridge.lock().unwrap();
f(&mut _bridge);
(0, bridge.clone())
}
pub fn add_window(
&mut self,
id: WindowId,
bridge: TextureBridge,
) -> (usize, Arc<Mutex<TextureBridge>>) {
let bridge = Arc::new(Mutex::new(bridge));
pub fn add_window(&mut self, window: TAreaRef) -> WindowId {
// New window
self.inner
.entry(id)
.and_modify(|e| *e = bridge.clone())
.or_insert(bridge.clone());
let new_id = self.all_bridges.len();
self.all_bridges.push((new_id, bridge.clone()));
let buffer = {
let bridge = bridge.lock().unwrap();
bridge.buffer()
};
self.all_revelant_buffers
.lock()
.unwrap()
.insert(new_id, buffer);
(new_id, bridge)
let id = WindowId::primary();
self.inner.entry(id.clone()).or_insert(window);
id
}
pub fn get_bridge(&self, id: usize) -> Option<Arc<Mutex<TextureBridge>>> {
self.all_bridges.get(id).map(|(_, bridge)| bridge.clone())
pub fn get_window(&self, id: &WindowId) -> Option<&TAreaRef> {
self.inner.get(id)
}
pub fn buffer(&self) -> AllBuffers {
self.all_revelant_buffers.clone()
pub fn sender(&self) -> ToUISender<(TaskId, Vec<f32>)> {
self.recv.sender()
}
pub fn try_deal_render_result(&mut self, cx: &mut Cx, bridge_manager: &BridgeManager) {
while let Ok((id, data)) = self.recv.try_recv() {
let windows = bridge_manager.get_net(&id).unwrap();
for window in windows {
let window = self.get_window(window).unwrap();
let texture = window.get_texture();
if texture.is_none() {
let texture = Texture::new_with_format(
cx,
makepad_widgets::TextureFormat::VecRGBAf32 {
width: 1920,
height: 1080,
data: None,
updated: makepad_widgets::TextureUpdated::Full,
},
);
window.set_texture(cx, texture);
}
let texture = window.get_texture().unwrap();
texture.put_back_vec_f32(
cx,
data.clone(),
Some(RectUsize::new(
PointUsize::new(0, 0),
SizeUsize::new(1920, 1080),
)),
);
window.redraw(cx);
}
}
}
}
impl WindowId {
pub fn new<S: Into<String>>(name: S) -> Self {
Self {
id: 0,
id: WINDOW_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
name: name.into(),
}
}
}
#[derive(Clone, Default)]
pub struct WM(Arc<std::sync::Mutex<WindowsManager>>);
impl WM {
pub fn new() -> Self {
Self(Arc::new(std::sync::Mutex::new(WindowsManager::new())))
pub fn primary() -> Self {
Self {
id: WINDOW_ID.fetch_add(1, std::sync::atomic::Ordering::SeqCst),
name: "Primary".into(),
}
pub fn windows_manager(&self) -> Arc<std::sync::Mutex<WindowsManager>> {
self.0.clone()
}
pub fn wm(&self) -> MutexGuard<'_, WindowsManager> {
self.0.lock().unwrap()
}
}

View File

@ -19,7 +19,7 @@ pub enum PipelineError {
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("")]
#[error("because: {0}")]
IOError(#[from] std::io::Error),
#[error("")]
TomlError(#[from] toml::de::Error),

View File

@ -14,6 +14,7 @@ image = "0.25.5"
rust-embed = "8.5.0"
flume = "0.11.1"
log = "0.4.22"
thiserror = "2.0.3"
[build-dependencies]
regex = "1.11.1"

263
mp_elements/config.toml Normal file
View File

@ -0,0 +1,263 @@
[common]
background = "Terrain"
path = "resources/alts.png"
plugins = "loaders"
[[cmap]]
type = "DBZ"
levels = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75]
colors = [
"#aaaaaa",
"#0022ff",
"#01a0f6",
"#00ecec",
"#00d800",
"#019000",
"#ffff00",
"#e7c000",
"#ff9000",
"#ff0000",
"#d60000",
"#c00000",
"#ff00f0",
"#9600b4",
"#ad90f0",
]
[[cmap]]
type = "VEL"
levels = [
-90,
-45,
-35,
-27,
-20,
-15,
-10,
-5,
-1,
0,
1,
5,
10,
15,
20,
27,
1000,
]
colors = [
"#9fffff",
"#00e0ff",
"#0080ff",
"#320096",
"#00fb90",
"#00bb90",
"#008f00",
"#cdc09f",
"#000000",
"#f88700",
"#ffcf00",
"#ffff00",
"#ae0000",
"#d07000",
"#dd0000",
"#ff0000",
]
[[cmap]]
type = "SW"
colors = [
"#E0E0E0",
"#7CE0E0",
"#00E0E0",
"#00B0B0",
"#00FEFE",
"#00C400",
"#008000",
"#FEFE00",
"#FED200",
"#FE7C00",
"#FEB0B0",
"#FE5858",
"#FE0000",
"#FEFEFE",
]
levels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
[[cmap]]
type = "CC"
colors = [
"#003CFF",
"#00EFEF",
"#00BABF",
"#00837D",
"#008938",
"#00B729",
"#00DA0D",
"#00FF00",
"#FFFF3B",
"#FFF000",
"#FFC600",
"#FFA500",
"#FF7200",
"#FF1F00",
"#C10000",
"#D400AA",
]
levels = [
0,
0.1,
0.3,
0.5,
0.6,
0.7,
0.8,
0.85,
0.9,
0.92,
0.94,
0.95,
0.96,
0.97,
0.98,
0.99,
]
[[cmap]]
type = "KDP"
colors = [
"#00FFFF",
"#00EFEF",
"#00A8AC",
"#B4B4B4",
"#B4B4B4",
"#00C027",
"#00E80A",
"#24FF24",
"#FFFF1E",
"#FFE600",
"#FFBC00",
"#FF9800",
"#FF5E00",
"#F20F00",
"#BB003A",
"#DD009C",
"#FF00FF",
]
levels = [
-0.8,
-0.4,
-0.2,
-0.1,
0.1,
0.15,
0.22,
0.33,
0.5,
0.75,
1.1,
1.7,
2.4,
3.1,
7,
20,
]
[[cmap]]
type = "ZDR"
colors = [
"#464646",
"#505050",
"#5A5A5A",
"#646464",
"#6E6E6E",
"#787878",
"#828282",
"#8C8C8C",
"#969696",
"#AFAFAF",
"#C8C8C8",
"#DCF0DC",
"#00C027",
"#00E80A",
"#24FF24",
"#FFFF1E",
"#FFF20F",
"#FFE600",
"#FFBC00",
"#FF9800",
"#FF5E00",
"#FFFF00",
"#F20F00",
"#BB003A",
"#DD009C",
"#FF00FF",
]
levels = [
-5,
-4.5,
-4,
-3.5,
-3,
-2.5,
-2,
-1.5,
-1,
-0.5,
0,
0.5,
1,
1.5,
2,
2.5,
3,
3.5,
4,
4.5,
5,
5.5,
6,
6.5,
7,
]
[[cmap]]
type = "VIL"
levels = [
0.1,
1,
2.5,
5,
7.5,
10,
10.25,
15,
20,
25,
30,
35,
40,
45,
50,
55,
104,
]
colors = [
'#484892',
'#01a0f6',
'#00ecec',
'#01ff00',
'#00c800',
'#019000',
'#ffff00',
'#e7c000',
'#ff9000',
'#ff0000',
'#d60000',
'#c00000',
'#ff00f0',
'#ad90f0',
'#780084',
'#d8af97',
]

View File

@ -1,3 +1,6 @@
use std::cell::Cell;
use std::sync::Mutex;
use crate::elements::{Element, ElementAttach, Elements};
use crate::elementvec::ElementVec;
use encase;
@ -63,8 +66,8 @@ impl App {
}
}
pub fn buffer_pool(&mut self) -> &mut DataBufferPool {
&mut self.buffer_pool
pub fn buffer_pool(&self) -> &DataBufferPool {
&self.buffer_pool
}
// Create a new context struct. This struct contains references to the device, queue, bind group layout, and bind group.
@ -127,6 +130,12 @@ impl App {
render_pass.set_bind_group(*index, bind_group, &[]);
}
// Set the scissors for the render pass.
{
let scissors = window.scissors.lock().unwrap();
render_pass.set_scissor_rect(0, 0, scissors[0], scissors[1]);
}
// Set the bind group for the render pass.
attach.bind(&mut render_pass);
@ -170,6 +179,7 @@ pub struct RenderWindow {
texture_view: wgpu::TextureView,
output: Output,
window: Window,
scissors: Mutex<[u32; 2]>,
}
impl RenderWindow {
@ -187,7 +197,8 @@ impl RenderWindow {
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Rgba8UnormSrgb,
// format: wgpu::TextureFormat::Rgba8UnormSrgb,
format: wgpu::TextureFormat::Rgba32Float,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
view_formats: &[],
});
@ -196,6 +207,7 @@ impl RenderWindow {
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let output = Output::new(device);
let scissors = Mutex::new([window.width, window.height]);
Self {
_texture: texture,
@ -203,6 +215,7 @@ impl RenderWindow {
texture_view,
output,
window,
scissors,
}
}
@ -211,6 +224,10 @@ impl RenderWindow {
}
pub fn finish(&self, encoder: &mut wgpu::CommandEncoder) {
let (width, height) = {
let l = self.scissors.lock().unwrap();
(l[0], l[1])
};
encoder.copy_texture_to_buffer(
wgpu::ImageCopyTexture {
texture: &self._texture,
@ -222,13 +239,13 @@ impl RenderWindow {
buffer: &self.output.output_buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: Some(256 * 4),
rows_per_image: Some(256),
bytes_per_row: Some((width * 16 + 255) / 256 * 256),
rows_per_image: Some(height),
},
},
wgpu::Extent3d {
width: self.window.width,
height: self.window.height,
width: width,
height: height,
depth_or_array_layers: 1,
},
);
@ -387,9 +404,9 @@ pub struct Output {
impl Output {
pub fn new(device: &wgpu::Device) -> Self {
let u32_size = std::mem::size_of::<u32>() as u32;
let f32_size = std::mem::size_of::<f32>() as u32;
let output_buffer_size = (u32_size * 256 * 256) as wgpu::BufferAddress;
let output_buffer_size = (f32_size * 4 * 1920 * 1080) as wgpu::BufferAddress;
let output_buffer_desc = wgpu::BufferDescriptor {
size: output_buffer_size,
usage: wgpu::BufferUsages::COPY_DST
@ -403,7 +420,7 @@ impl Output {
Self { output_buffer }
}
pub async fn get_data(&self, ctx: &Ctx) {
async fn get_data(&self, ctx: &Ctx) {
let device = &ctx.device;
// 需要对映射变量设置范围,以便我们能够解除缓冲区的映射
let buffer_slice = self.output_buffer.slice(..);

View File

@ -1,10 +1,15 @@
use std::sync::Arc;
use std::sync::{Arc, Mutex};
pub mod ppi;
use crate::{
app::{BufferKey, Ctx, DataBufferPool},
errors::ElementError,
App,
};
use mp_core::config;
use mp_core::{
config,
errors::{ConfigError, DataError},
Data, Setting,
};
pub use ppi::PPI;
use std::any::Any;
use wgpu::util::DeviceExt;
@ -78,6 +83,45 @@ macro_rules! elements {
$(Elements::$element_name(_) => <$element>::DESC,)+
}
}
pub fn load_data(&self,ctx:&Ctx,data: &Data, buffer_pool: &DataBufferPool, config: Option<&dyn Any>) -> Result<ElementAttach, DataError> {
Ok(match self {
$(Elements::$element_name(element) => {
if let Some(cfg) = config {
if let Some(cfg) = cfg.downcast_ref::<<$element as Element>::Config>(){
element.load_data(ctx, data.try_into()?, buffer_pool, cfg)
} else {
panic!("Invalid config type")
}
} else {
element.load_data(ctx, data.try_into()?, buffer_pool, &<$element as Element>::Config::default())
}
},)+
})
}
pub fn update_uniform(&self, ctx: &Ctx, attach: &ElementAttach, config: &dyn Any) {
match self {
$(Elements::$element_name(element) => {
if let Some(cfg) = config.downcast_ref::<<$element as Element>::Config>(){
element.update_uniform(ctx, attach, cfg )
} else {
panic!("Invalid config type")
}
})+
}
}
pub fn init_config(&self, data: &Data, setting: &Setting) -> Result<Arc<Mutex<dyn Any + Send + 'static>>, ElementError> {
Ok(match self {
$(Elements::$element_name(element) => {
let cfg = element.init_config(data.try_into()?, setting)?;
Arc::new(Mutex::new(cfg)) as Arc<Mutex<dyn Any + Send + 'static>>
})+
})
}
}
impl<'a> ElementsRef<'a> {
@ -116,7 +160,7 @@ pub trait Element {
type Vertex;
type Uniform;
type Data;
type Config;
type Config: Default + ElementConfig<Element = Self>;
const NAME: &'static str;
const DESC: &'static str;
@ -143,6 +187,28 @@ pub trait Element {
buffer_pool: &DataBufferPool,
config: &Self::Config,
) -> ElementAttach;
fn update_uniform(&self, ctx: &Ctx, attach: &ElementAttach, config: &Self::Config);
fn init_config(
&self,
data: &Self::Data,
setting: &Setting,
) -> Result<Self::Config, ConfigError> {
let mut default_config = Self::Config::default();
default_config.init(data, setting)?;
Ok(default_config)
}
}
pub trait ElementConfig {
type Element: Element;
fn init(
&mut self,
data: &<Self::Element as Element>::Data,
setting: &Setting,
) -> Result<(), ConfigError>;
}
#[derive(Debug)]

View File

@ -8,10 +8,10 @@ use crate::{
};
use bytemuck;
use glam::{Vec3, Vec4};
use mp_core::{data::CoordType, RadarGridData};
use mp_core::{data::CoordType, errors::ConfigError, RadarGridData};
use wgpu::util::DeviceExt;
use super::{Element, ElementAttach};
use super::{Element, ElementAttach, ElementConfig};
const EMAXNUM: u64 = 50;
const AMAXNUM: u64 = 360;
@ -24,15 +24,51 @@ pub struct PPI {
colormap: Option<ColorMap>,
}
#[derive(Default)]
pub struct PPIConfig {
pub colormap: Vec<[f32; 4]>,
pub color_range: [f32; 2],
pub layer: f32,
pub three_d: bool,
pub scale: f32,
pub size: f32,
}
impl ElementConfig for PPIConfig {
type Element = PPI;
fn init(
&mut self,
data: &<Self::Element as Element>::Data,
setting: &mp_core::Setting,
) -> Result<(), ConfigError> {
let data_type = &data.data_type();
let cb = setting.find(data_type);
if let Some(cb) = cb {
let colormaps = cb.color()?;
self.colormap = colormaps
.iter()
.map(|c| {
[
c[0] as f32 / 255.0,
c[1] as f32 / 255.0,
c[2] as f32 / 255.0,
c[3] as f32 / 255.0,
]
})
.collect();
}
Ok(())
}
}
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct PPIUniform {
origin: Vec4,
common_cfg: [f32; 4],
}
#[repr(C)]
@ -145,7 +181,8 @@ impl Element for PPI {
compilation_options: Default::default(),
entry_point: Some("fragment"),
targets: &[Some(wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba8UnormSrgb,
// format: wgpu::TextureFormat::Rgba8UnormSrgb,
format: wgpu::TextureFormat::Rgba32Float,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
@ -219,6 +256,7 @@ impl Element for PPI {
label: Some("PPI Uniform Buffer"),
contents: bytemuck::cast_slice(&[PPIUniform {
origin: Vec4::new(0.0, 0.0, 0.0, 0.0),
common_cfg: [0.0, 0.0, 0.0, 0.0],
}]),
usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::UNIFORM,
});
@ -286,6 +324,19 @@ impl Element for PPI {
data_buffer_key: Some(key),
}
}
fn update_uniform(&self, ctx: &Ctx, attach: &ElementAttach, config: &Self::Config) {
let uniform_buffer = attach.uniform_buffer.as_ref().unwrap();
let uniform = PPIUniform {
origin: Vec4::new(0.0, 0.0, 0.0, 0.0),
common_cfg: [0.0, 0.0, 0.0, 0.0],
};
let queue = &ctx.queue;
queue.write_buffer(uniform_buffer, 0, bytemuck::cast_slice(&[uniform]));
}
}
impl PPI {

10
mp_elements/src/errors.rs Normal file
View File

@ -0,0 +1,10 @@
use mp_core::errors::{ConfigError, DataError};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ElementError {
#[error("Init Failed, {0}")]
ConfigFailed(#[from] ConfigError),
#[error("Data Failed, {0}")]
DataFailed(#[from] DataError),
}

View File

@ -1,6 +1,7 @@
pub mod app;
pub mod elements;
pub mod elementvec;
pub mod errors;
pub mod renderer;
pub mod tools;
mod utils;

0
test.png Normal file
View File