selected_var_format_button
This commit is contained in:
parent
fbdf8090a1
commit
a6da2887c9
13 changed files with 191 additions and 87 deletions
|
@ -13,7 +13,6 @@ wasm-bindgen-test = "0.3.19"
|
|||
[dependencies]
|
||||
zoon.workspace = true
|
||||
wellen.workspace = true
|
||||
convert-base.workspace = true
|
||||
shared = { path = "../shared", features = ["frontend"] }
|
||||
web-sys = { version = "*", features = ["FileSystemFileHandle"] }
|
||||
gloo-file = { version = "0.3.0", features = ["futures"] }
|
||||
|
|
|
@ -170,7 +170,8 @@ impl ControlsPanel {
|
|||
Label::new()
|
||||
.s(Padding::new().x(20).y(10))
|
||||
.s(Background::new().color_signal(
|
||||
hovered_signal.map_bool(|| color!("MediumSlateBlue"), || color!("SlateBlue")),
|
||||
hovered_signal
|
||||
.map_bool(|| color!("MediumSlateBlue"), || color!("SlateBlue")),
|
||||
))
|
||||
.s(Align::new().left())
|
||||
.s(RoundedCorners::all(15))
|
||||
|
@ -183,47 +184,52 @@ impl ControlsPanel {
|
|||
))
|
||||
.on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered))
|
||||
.for_input(file_input_id)
|
||||
.on_click_event_with_options(EventOptions::new().preventable(), clone!((hierarchy) move |event| {
|
||||
let mut hierarchy_lock = hierarchy.lock_mut();
|
||||
if hierarchy_lock.is_some() {
|
||||
*hierarchy_lock = None;
|
||||
if let RawMouseEvent::Click(raw_event) = event.raw_event {
|
||||
// @TODO Move to MoonZoon as a new API
|
||||
raw_event.prevent_default();
|
||||
.on_click_event_with_options(
|
||||
EventOptions::new().preventable(),
|
||||
clone!((hierarchy) move |event| {
|
||||
let mut hierarchy_lock = hierarchy.lock_mut();
|
||||
if hierarchy_lock.is_some() {
|
||||
*hierarchy_lock = None;
|
||||
if let RawMouseEvent::Click(raw_event) = event.raw_event {
|
||||
// @TODO Move to MoonZoon as a new API
|
||||
raw_event.prevent_default();
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}))
|
||||
}),
|
||||
),
|
||||
)
|
||||
.item(
|
||||
// @TODO https://github.com/MoonZoon/MoonZoon/issues/39
|
||||
// + https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications#using_hidden_file_input_elements_using_the_click_method
|
||||
TextInput::new()
|
||||
.id(file_input_id)
|
||||
.update_raw_el(|raw_el| {
|
||||
let dom_element = raw_el.dom_element();
|
||||
raw_el
|
||||
.style("display", "none")
|
||||
.attr("type", "file")
|
||||
.event_handler(move |_: events::Input| {
|
||||
let Some(file_list) = dom_element.files().map(gloo_file::FileList::from) else {
|
||||
zoon::println!("file list is `None`");
|
||||
return;
|
||||
};
|
||||
let Some(file) = file_list.first().cloned() else {
|
||||
zoon::println!("file list is empty");
|
||||
return;
|
||||
};
|
||||
let hierarchy = hierarchy.clone();
|
||||
let loaded_filename = loaded_filename.clone();
|
||||
Task::start(async move {
|
||||
if let Some(filename) = platform::pick_and_load_waveform(Some(file)).await {
|
||||
loaded_filename.set_neq(Some(filename));
|
||||
hierarchy.set(Some(Rc::new(platform::get_hierarchy().await)))
|
||||
}
|
||||
})
|
||||
TextInput::new().id(file_input_id).update_raw_el(|raw_el| {
|
||||
let dom_element = raw_el.dom_element();
|
||||
raw_el
|
||||
.style("display", "none")
|
||||
.attr("type", "file")
|
||||
.event_handler(move |_: events::Input| {
|
||||
let Some(file_list) =
|
||||
dom_element.files().map(gloo_file::FileList::from)
|
||||
else {
|
||||
zoon::println!("file list is `None`");
|
||||
return;
|
||||
};
|
||||
let Some(file) = file_list.first().cloned() else {
|
||||
zoon::println!("file list is empty");
|
||||
return;
|
||||
};
|
||||
let hierarchy = hierarchy.clone();
|
||||
let loaded_filename = loaded_filename.clone();
|
||||
Task::start(async move {
|
||||
if let Some(filename) =
|
||||
platform::pick_and_load_waveform(Some(file)).await
|
||||
{
|
||||
loaded_filename.set_neq(Some(filename));
|
||||
hierarchy.set(Some(Rc::new(platform::get_hierarchy().await)))
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use zoon::*;
|
||||
use std::rc::Rc;
|
||||
use zoon::*;
|
||||
|
||||
mod platform;
|
||||
|
||||
|
@ -42,13 +42,22 @@ fn root() -> impl Element {
|
|||
selected_var_refs.clone(),
|
||||
layout.clone(),
|
||||
))
|
||||
.item_signal(layout.signal().map(|layout| matches!(layout, Layout::Tree)).map_true(clone!((hierarchy, selected_var_refs) move || WaveformPanel::new(
|
||||
hierarchy.clone(),
|
||||
selected_var_refs.clone(),
|
||||
))))
|
||||
.item_signal(
|
||||
layout
|
||||
.signal()
|
||||
.map(|layout| matches!(layout, Layout::Tree))
|
||||
.map_true(
|
||||
clone!((hierarchy, selected_var_refs) move || WaveformPanel::new(
|
||||
hierarchy.clone(),
|
||||
selected_var_refs.clone(),
|
||||
)),
|
||||
),
|
||||
),
|
||||
)
|
||||
.item_signal(
|
||||
layout
|
||||
.signal()
|
||||
.map(|layout| matches!(layout, Layout::Columns))
|
||||
.map_true(move || WaveformPanel::new(hierarchy.clone(), selected_var_refs.clone())),
|
||||
)
|
||||
.item_signal(layout.signal().map(|layout| matches!(layout, Layout::Columns)).map_true(move || WaveformPanel::new(
|
||||
hierarchy.clone(),
|
||||
selected_var_refs.clone(),
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -137,13 +137,7 @@ fn signal_to_timeline(
|
|||
continue;
|
||||
}
|
||||
|
||||
// @TODO dynamic formatter
|
||||
// @TODO optimize it by not using `.to_string` if possible
|
||||
let value = value.to_string();
|
||||
let ones_and_zeros = value.chars().rev().map(|char| char.to_digit(2).unwrap()).collect::<Vec<_>>();
|
||||
let mut base = convert_base::Convert::new(2, 16);
|
||||
let output = base.convert::<u32, u32>(&ones_and_zeros);
|
||||
let value: String = output.into_iter().map(|number| char::from_digit(number, 16).unwrap()).collect();
|
||||
let value = shared::VarFormat::default().format(value);
|
||||
|
||||
let value_width = value.chars().count() as u32 * LETTER_WIDTH;
|
||||
let label = if (value_width + (2 * LABEL_X_PADDING)) <= block_width {
|
||||
|
|
|
@ -126,7 +126,7 @@ impl WaveformPanel {
|
|||
zoon::println!("{timescale:?}");
|
||||
|
||||
// Note: Sync `timeline`'s type with the `Timeline` in `frontend/typescript/pixi_canvas/pixi_canvas.ts'
|
||||
let timeline = serde_wasm_bindgen::to_value(&timeline).unwrap_throw();
|
||||
let timeline = serde_wasm_bindgen::to_value(&timeline).unwrap_throw();
|
||||
let signal_ref_index = signal_ref.index();
|
||||
controller.push_var(signal_ref_index, timeline);
|
||||
}
|
||||
|
@ -140,23 +140,56 @@ impl WaveformPanel {
|
|||
None?
|
||||
};
|
||||
let var = hierarchy.get(var_ref);
|
||||
let name: &str = var.name(&hierarchy);
|
||||
Row::new()
|
||||
.item(self.selected_var_name_button(var.name(&hierarchy), index))
|
||||
.item(self.selected_var_format_button())
|
||||
.apply(Some)
|
||||
}
|
||||
|
||||
fn selected_var_name_button(
|
||||
&self,
|
||||
name: &str,
|
||||
index: ReadOnlyMutable<Option<usize>>,
|
||||
) -> impl Element {
|
||||
let selected_var_refs = self.selected_var_refs.clone();
|
||||
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
|
||||
Button::new()
|
||||
.s(Height::exact(ROW_HEIGHT))
|
||||
.s(Background::new().color(color!("SlateBlue", 0.8)))
|
||||
.s(RoundedCorners::new().left(15))
|
||||
.s(Background::new().color_signal(
|
||||
hovered_signal.map_bool(|| color!("SlateBlue"), || color!("SlateBlue", 0.8)),
|
||||
))
|
||||
.s(RoundedCorners::new().left(15).right(5))
|
||||
.label(
|
||||
El::new()
|
||||
.s(Align::center())
|
||||
.s(Padding::new().left(20).right(17).y(10))
|
||||
.child(name),
|
||||
)
|
||||
.on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered))
|
||||
.on_press(move || {
|
||||
if let Some(index) = index.get() {
|
||||
selected_var_refs.lock_mut().remove(index);
|
||||
}
|
||||
})
|
||||
.apply(Some)
|
||||
}
|
||||
|
||||
fn selected_var_format_button(&self) -> impl Element {
|
||||
let var_format = Mutable::new(shared::VarFormat::default());
|
||||
let (hovered, hovered_signal) = Mutable::new_and_signal(false);
|
||||
Button::new()
|
||||
.s(Height::exact(ROW_HEIGHT))
|
||||
.s(Width::exact(70))
|
||||
.s(Background::new().color_signal(
|
||||
hovered_signal.map_bool(|| color!("SlateBlue"), || color!("SlateBlue", 0.8)),
|
||||
))
|
||||
.s(RoundedCorners::new().left(5))
|
||||
.label(
|
||||
El::new()
|
||||
.s(Align::center())
|
||||
.s(Padding::new().left(20).right(17).y(10))
|
||||
.child_signal(var_format.signal().map(|format| format.as_static_str())),
|
||||
)
|
||||
.on_hovered_change(move |is_hovered| hovered.set_neq(is_hovered))
|
||||
.on_press(move || var_format.update(|format| format.next()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
pub use js_bridge::PixiController;
|
||||
use zoon::*;
|
||||
use std::rc::Rc;
|
||||
use crate::platform;
|
||||
pub use js_bridge::PixiController;
|
||||
use std::rc::Rc;
|
||||
use zoon::*;
|
||||
|
||||
pub struct PixiCanvas {
|
||||
raw_el: RawHtmlEl<web_sys::HtmlElement>,
|
||||
|
@ -41,22 +41,31 @@ impl PixiCanvas {
|
|||
let height = height.signal() => (*width, *height)
|
||||
}
|
||||
.throttle(|| Timer::sleep(50))
|
||||
.for_each(clone!((controller) move |(width, height)| clone!((controller) async move {
|
||||
if let Some(controller) = controller.lock_ref().as_ref() {
|
||||
controller.resize(width, height).await
|
||||
}
|
||||
}))),
|
||||
.for_each(
|
||||
clone!((controller) move |(width, height)| clone!((controller) async move {
|
||||
if let Some(controller) = controller.lock_ref().as_ref() {
|
||||
controller.resize(width, height).await
|
||||
}
|
||||
})),
|
||||
),
|
||||
);
|
||||
let task_with_controller = Mutable::new(None);
|
||||
// -- FastWave-specific --
|
||||
let timeline_getter = Rc::new(Closure::new(|signal_ref_index, screen_width, row_height| {
|
||||
future_to_promise(async move {
|
||||
let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap_throw();
|
||||
let timeline = platform::load_signal_and_get_timeline(signal_ref, screen_width, row_height).await;
|
||||
let timeline = serde_wasm_bindgen::to_value(&timeline).unwrap_throw();
|
||||
Ok(timeline)
|
||||
})
|
||||
}));
|
||||
let timeline_getter = Rc::new(Closure::new(
|
||||
|signal_ref_index, screen_width, row_height| {
|
||||
future_to_promise(async move {
|
||||
let signal_ref = wellen::SignalRef::from_index(signal_ref_index).unwrap_throw();
|
||||
let timeline = platform::load_signal_and_get_timeline(
|
||||
signal_ref,
|
||||
screen_width,
|
||||
row_height,
|
||||
)
|
||||
.await;
|
||||
let timeline = serde_wasm_bindgen::to_value(&timeline).unwrap_throw();
|
||||
Ok(timeline)
|
||||
})
|
||||
},
|
||||
));
|
||||
// -- // --
|
||||
Self {
|
||||
controller: controller.read_only(),
|
||||
|
@ -105,7 +114,8 @@ mod js_bridge {
|
|||
type SignalRefIndex = usize;
|
||||
type ScreenWidth = u32;
|
||||
type RowHeight = u32;
|
||||
type TimelineGetter = Closure<dyn FnMut(SignalRefIndex, ScreenWidth, RowHeight) -> TimelinePromise>;
|
||||
type TimelineGetter =
|
||||
Closure<dyn FnMut(SignalRefIndex, ScreenWidth, RowHeight) -> TimelinePromise>;
|
||||
|
||||
// Note: Add all corresponding methods to `frontend/typescript/pixi_canvas/pixi_canvas.ts`
|
||||
#[wasm_bindgen(module = "/typescript/bundles/pixi_canvas.js")]
|
||||
|
@ -115,7 +125,11 @@ mod js_bridge {
|
|||
|
||||
// @TODO `row_height` and `row_gap` is FastWave-specific
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(row_height: u32, row_gap: u32, timeline_getter: &TimelineGetter) -> PixiController;
|
||||
pub fn new(
|
||||
row_height: u32,
|
||||
row_gap: u32,
|
||||
timeline_getter: &TimelineGetter,
|
||||
) -> PixiController;
|
||||
|
||||
#[wasm_bindgen(method)]
|
||||
pub async fn init(this: &PixiController, parent_element: &JsValue);
|
||||
|
|
Reference in a new issue