make TUI widgets cross framework
This commit is contained in:
1 parent
9a16687bdc
commit
625dc662d2
32 files changed
+3023
-1091
No files matched your search
@@ -0,0 +1,204 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use dioxus_html::input_data::keyboard_types::Key;
|
||||
use dioxus_native_core::{
|
||||
node::OwnedAttributeDiscription,
|
||||
node_ref::AttributeMask,
|
||||
prelude::NodeType,
|
||||
real_dom::{ElementNodeMut, NodeImmutable, NodeTypeMut, RealDom},
|
||||
utils::widget_watcher::Widget,
|
||||
NodeId,
|
||||
};
|
||||
use shipyard::UniqueViewMut;
|
||||
|
||||
use crate::FormData;
|
||||
|
||||
use super::{RinkWidget, WidgetContext};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct Button {
|
||||
div_id: NodeId,
|
||||
text_id: NodeId,
|
||||
value: String,
|
||||
}
|
||||
|
||||
impl Button {
|
||||
fn width(el: &ElementNodeMut) -> String {
|
||||
if let Some(value) = el
|
||||
.get_attribute(&OwnedAttributeDiscription {
|
||||
name: "width".to_string(),
|
||||
namespace: None,
|
||||
})
|
||||
.and_then(|value| value.as_text())
|
||||
.map(|value| value.to_string())
|
||||
{
|
||||
value
|
||||
} else {
|
||||
"1px".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn height(el: &ElementNodeMut) -> String {
|
||||
if let Some(value) = el
|
||||
.get_attribute(&OwnedAttributeDiscription {
|
||||
name: "height".to_string(),
|
||||
namespace: None,
|
||||
})
|
||||
.and_then(|value| value.as_text())
|
||||
.map(|value| value.to_string())
|
||||
{
|
||||
value
|
||||
} else {
|
||||
"1px".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn update_size_attr(&mut self, el: &mut ElementNodeMut) {
|
||||
let width = Self::width(el);
|
||||
let height = Self::height(el);
|
||||
let single_char = width == "1px" || height == "1px";
|
||||
let border_style = if single_char { "none" } else { "solid" };
|
||||
el.set_attribute(
|
||||
OwnedAttributeDiscription {
|
||||
name: "border-style".to_string(),
|
||||
namespace: Some("style".to_string()),
|
||||
},
|
||||
border_style.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
fn update_value_attr(&mut self, el: &ElementNodeMut) {
|
||||
if let Some(value) = el
|
||||
.get_attribute(&OwnedAttributeDiscription {
|
||||
name: "value".to_string(),
|
||||
namespace: None,
|
||||
})
|
||||
.and_then(|value| value.as_text())
|
||||
.map(|value| value.to_string())
|
||||
{
|
||||
self.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
fn write_value(&self, rdom: &mut RealDom) {
|
||||
if let Some(mut text) = rdom.get_mut(self.text_id) {
|
||||
let node_type = text.node_type_mut();
|
||||
let NodeTypeMut::Text(mut text) = node_type else { panic!("input must be an element") };
|
||||
*text.text_mut() = self.value.clone();
|
||||
}
|
||||
}
|
||||
|
||||
fn switch(&mut self, ctx: &mut WidgetContext) {
|
||||
let data = FormData {
|
||||
value: self.value.to_string(),
|
||||
values: HashMap::new(),
|
||||
files: None,
|
||||
};
|
||||
ctx.send(crate::Event {
|
||||
id: self.div_id,
|
||||
name: "input",
|
||||
data: crate::EventData::Form(data),
|
||||
bubbles: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Button {
|
||||
const NAME: &'static str = "input";
|
||||
|
||||
fn create(root: &mut dioxus_native_core::real_dom::NodeMut<()>) -> Self {
|
||||
let node_type = root.node_type();
|
||||
let NodeType::Element(el) = &*node_type else { panic!("input must be an element") };
|
||||
|
||||
let value = el
|
||||
.attributes
|
||||
.get(&OwnedAttributeDiscription {
|
||||
name: "value".to_string(),
|
||||
namespace: None,
|
||||
})
|
||||
.and_then(|value| value.as_text())
|
||||
.map(|value| value.to_string());
|
||||
|
||||
drop(node_type);
|
||||
|
||||
let rdom = root.real_dom_mut();
|
||||
let text = rdom.create_node(value.clone().unwrap_or_default());
|
||||
let text_id = text.id();
|
||||
|
||||
root.add_event_listener("keydown");
|
||||
root.add_event_listener("click");
|
||||
let div_id = root.id();
|
||||
root.add_child(text_id);
|
||||
|
||||
Self {
|
||||
div_id,
|
||||
text_id,
|
||||
value: value.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn attributes_changed(
|
||||
&mut self,
|
||||
mut root: dioxus_native_core::real_dom::NodeMut<()>,
|
||||
attributes: &dioxus_native_core::node_ref::AttributeMask,
|
||||
) {
|
||||
match attributes {
|
||||
AttributeMask::All => {
|
||||
{
|
||||
let node_type = root.node_type_mut();
|
||||
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
|
||||
self.update_value_attr(&el);
|
||||
self.update_size_attr(&mut el);
|
||||
}
|
||||
self.write_value(root.real_dom_mut());
|
||||
}
|
||||
AttributeMask::Some(attrs) => {
|
||||
{
|
||||
let node_type = root.node_type_mut();
|
||||
let NodeTypeMut::Element(mut el) = node_type else { panic!("input must be an element") };
|
||||
if attrs.contains("width") || attrs.contains("height") {
|
||||
self.update_size_attr(&mut el);
|
||||
}
|
||||
if attrs.contains("value") {
|
||||
self.update_value_attr(&el);
|
||||
}
|
||||
}
|
||||
if attrs.contains("value") {
|
||||
self.write_value(root.real_dom_mut());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RinkWidget for Button {
|
||||
fn handle_event(
|
||||
&mut self,
|
||||
event: &crate::Event,
|
||||
node: &mut dioxus_native_core::real_dom::NodeMut,
|
||||
) {
|
||||
let mut ctx: UniqueViewMut<WidgetContext> = node
|
||||
.real_dom_mut()
|
||||
.raw_world_mut()
|
||||
.borrow()
|
||||
.expect("expected widget context");
|
||||
|
||||
match event.name {
|
||||
"click" => self.switch(&mut ctx),
|
||||
"keydown" => {
|
||||
if let crate::EventData::Keyboard(data) = &event.data {
|
||||
if !data.is_auto_repeating()
|
||||
&& match data.key() {
|
||||
Key::Character(c) if c == " " => true,
|
||||
Key::Enter => true,
|
||||
_ => false,
|
||||
}
|
||||
{
|
||||
self.switch(&mut ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user