wallet: add Property wrapper types, and begin prelim selection code

This commit is contained in:
darkfi
2024-05-30 15:38:49 +02:00
parent cb81b93e65
commit d0303017a0
6 changed files with 425 additions and 84 deletions

View File

@@ -507,6 +507,15 @@ def draw():
node_id = api.add_node("editz", SceneNodeType.EDIT_BOX)
prop = Property(
"is_active", PropertyType.BOOL, PropertySubType.NULL,
None,
"Is Active", "Whether the editbox is active",
False, True, 1, None, None, []
)
api.add_property(node_id, prop)
api.set_property_bool(node_id, "is_active", 0, True)
prop = Property(
"rect", PropertyType.FLOAT32, PropertySubType.PIXEL,
None,
@@ -563,16 +572,38 @@ def draw():
api.set_property_str(node_id, "text", 0, "hello king!😁🍆jelly 🍆1234")
prop = Property(
"color", PropertyType.FLOAT32, PropertySubType.COLOR,
"text_color", PropertyType.FLOAT32, PropertySubType.COLOR,
None,
"Color", "Color of the text",
"Text Color", "Color of the text",
False, False, 4, 0, 1, []
)
api.add_property(node_id, prop)
api.set_property_f32(node_id, "color", 0, 1)
api.set_property_f32(node_id, "color", 1, 1)
api.set_property_f32(node_id, "color", 2, 1)
api.set_property_f32(node_id, "color", 3, 1)
api.set_property_f32(node_id, "text_color", 0, 1)
api.set_property_f32(node_id, "text_color", 1, 1)
api.set_property_f32(node_id, "text_color", 2, 1)
api.set_property_f32(node_id, "text_color", 3, 1)
prop = Property(
"hi_bg_color", PropertyType.FLOAT32, PropertySubType.COLOR,
None,
"Highlight Bg Color", "Background color for highlighted text",
False, False, 4, 0, 1, []
)
api.add_property(node_id, prop)
api.set_property_f32(node_id, "hi_bg_color", 0, 1)
api.set_property_f32(node_id, "hi_bg_color", 1, 1)
api.set_property_f32(node_id, "hi_bg_color", 2, 1)
api.set_property_f32(node_id, "hi_bg_color", 3, 0.5)
prop = Property(
"selected", PropertyType.UINT32, PropertySubType.NULL,
None,
"Selected", "Selected range",
True, False, 2, 0, None, []
)
api.add_property(node_id, prop)
api.set_property_u32(node_id, "selected", 0, 1)
api.set_property_u32(node_id, "selected", 1, 4)
prop = Property(
"z_index", PropertyType.UINT32, PropertySubType.NULL,

View File

@@ -1,20 +1,29 @@
use miniquad::{KeyMods, UniformType};
use miniquad::{KeyMods, UniformType, MouseButton};
use std::{io::Cursor, sync::{Arc, Mutex}};
use darkfi_serial::Decodable;
use freetype as ft;
use crate::{error::{Error, Result}, prop::Property, scene::{SceneGraph, SceneNodeId, Pimpl, Slot}, gfx::{Rectangle, RenderContext, COLOR_WHITE, COLOR_BLUE, COLOR_RED, COLOR_GREEN, FreetypeFace}, text::{Glyph, TextShaper}};
use crate::{error::{Error, Result}, prop::{
PropertyBool, PropertyFloat32, PropertyUint32, PropertyStr, PropertyColor,
Property}, scene::{SceneGraph, SceneNode, SceneNodeId, Pimpl, Slot}, gfx::{Rectangle, RenderContext, COLOR_WHITE, COLOR_BLUE, COLOR_RED, COLOR_GREEN, FreetypeFace, Point}, text::{Glyph, TextShaper}, keysym::{MouseButtonAsU8, KeyCodeAsU16}};
const CURSOR_WIDTH: f32 = 4.;
pub type EditBoxPtr = Arc<EditBox>;
pub struct EditBox {
scroll: Arc<Property>,
cursor_pos: Arc<Property>,
text: Arc<Property>,
font_size: Arc<Property>,
color: Arc<Property>,
is_active: PropertyBool,
debug: PropertyBool,
baseline: PropertyFloat32,
scroll: PropertyFloat32,
cursor_pos: PropertyUint32,
selected: Arc<Property>,
text: PropertyStr,
font_size: PropertyFloat32,
text_color: PropertyColor,
hi_bg_color: PropertyColor,
// Used for mouse clicks
world_rect: Mutex<Rectangle<f32>>,
glyphs: Mutex<Vec<Glyph>>,
text_shaper: TextShaper,
}
@@ -22,32 +31,40 @@ pub struct EditBox {
impl EditBox {
pub fn new(scene_graph: &mut SceneGraph, node_id: SceneNodeId, font_faces: Vec<FreetypeFace>) -> Result<Pimpl> {
let node = scene_graph.get_node(node_id).unwrap();
let scroll = node.get_property("scroll").ok_or(Error::PropertyNotFound)?;
let cursor_pos = node.get_property("cursor_pos").ok_or(Error::PropertyNotFound)?;
let text = node.get_property("text").ok_or(Error::PropertyNotFound)?;
let font_size = node.get_property("font_size").ok_or(Error::PropertyNotFound)?;
let color = node.get_property("color").ok_or(Error::PropertyNotFound)?;
let is_active = PropertyBool::wrap(node, "is_active", 0)?;
let debug = PropertyBool::wrap(node, "debug", 0)?;
let baseline = PropertyFloat32::wrap(node, "baseline", 0)?;
let scroll = PropertyFloat32::wrap(node, "scroll", 0)?;
let cursor_pos = PropertyUint32::wrap(node, "cursor_pos", 0)?;
let selected = node.get_property("selected").ok_or(Error::PropertyNotFound)?;
let text = PropertyStr::wrap(node, "text", 0)?;
let font_size = PropertyFloat32::wrap(node, "font_size", 0)?;
let text_color = PropertyColor::wrap(node, "text_color")?;
let hi_bg_color = PropertyColor::wrap(node, "hi_bg_color")?;
let text_shaper = TextShaper {
font_faces
};
let glyphs = text_shaper.shape(text.get_str(0)?, font_size.get_f32(0)?,
[color.get_f32(0)?, color.get_f32(1)?,
color.get_f32(2)?, color.get_f32(3)?]);
println!("EditBox::new()");
let self_ = Arc::new(Self{
is_active,
debug,
baseline,
scroll,
cursor_pos,
selected,
text,
font_size,
color,
glyphs: Mutex::new(glyphs),
text_color,
hi_bg_color,
world_rect: Mutex::new(Rectangle { x: 0., y: 0., w: 0., h: 0. }),
glyphs: Mutex::new(vec![]),
text_shaper,
});
let weak_self = Arc::downgrade(&self_);
self_.regen_glyphs().unwrap();
let weak_self = Arc::downgrade(&self_);
let slot = Slot {
name: "editbox::key_down".to_string(),
func: Box::new(move |data| {
@@ -74,15 +91,76 @@ impl EditBox {
.expect("no keyboard attached!");
keyb_node.register("key_down", slot);
let weak_self = Arc::downgrade(&self_);
let slot_btn_down = Slot {
name: "editbox::mouse_button_down".to_string(),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let button = MouseButton::from_u8(u8::decode(&mut cur).unwrap());
let x = f32::decode(&mut cur).unwrap();
let y = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.mouse_button_down(button, x, y);
}
}),
};
let weak_self = Arc::downgrade(&self_);
let slot_btn_up = Slot {
name: "editbox::mouse_button_up".to_string(),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let button = MouseButton::from_u8(u8::decode(&mut cur).unwrap());
let x = f32::decode(&mut cur).unwrap();
let y = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.mouse_button_up(button, x, y);
}
}),
};
let weak_self = Arc::downgrade(&self_);
let slot_move = Slot {
name: "editbox::mouse_move".to_string(),
func: Box::new(move |data| {
let mut cur = Cursor::new(&data);
let x = f32::decode(&mut cur).unwrap();
let y = f32::decode(&mut cur).unwrap();
let self_ = weak_self.upgrade();
if let Some(self_) = self_ {
self_.mouse_move(x, y);
}
}),
};
let mouse_node =
scene_graph
.lookup_node_mut("/window/input/mouse")
.expect("no mouse attached!");
mouse_node.register("button_down", slot_btn_down);
mouse_node.register("button_up", slot_btn_up);
mouse_node.register("move", slot_move);
// Save any properties we use
Ok(Pimpl::EditBox(self_))
}
pub fn render<'a>(&self, render: &mut RenderContext<'a>, node_id: SceneNodeId, layer_rect: &Rectangle<i32>) -> Result<()> {
pub fn render<'a>(&self, render: &mut RenderContext<'a>, node_id: SceneNodeId, layer_rect: &Rectangle<f32>) -> Result<()> {
let node = render.scene_graph.get_node(node_id).unwrap();
let rect = RenderContext::get_dim(node, layer_rect)?;
// Used for detecting mouse clicks
let mut world_rect = rect.clone();
world_rect.x += layer_rect.x as f32;
world_rect.y += layer_rect.y as f32;
*self.world_rect.lock().unwrap() = world_rect;
let layer_w = layer_rect.w as f32;
let layer_h = layer_rect.h as f32;
let off_x = rect.x / layer_w;
@@ -106,24 +184,20 @@ impl EditBox {
self.apply_cursor_scrolling(&rect);
let node = render.scene_graph.get_node(node_id).unwrap();
let text = node.get_property_str("text")?;
let font_size = node.get_property_f32("font_size")?;
let debug = node.get_property_bool("debug")?;
let baseline = node.get_property_f32("baseline")?;
let scroll = node.get_property_f32("scroll")?;
let cursor_pos = node.get_property_u32("cursor_pos")?;
let debug = self.debug.get();
let baseline = self.baseline.get();
let scroll = self.scroll.get();
let cursor_pos = self.cursor_pos.get() as usize;
let color_prop = node.get_property("color").ok_or(Error::PropertyNotFound)?;
let color_r = color_prop.get_f32(0)?;
let color_g = color_prop.get_f32(1)?;
let color_b = color_prop.get_f32(2)?;
let color_a = color_prop.get_f32(3)?;
let text_color = [color_r, color_g, color_b, color_a];
let color = node.get_property("text_color").ok_or(Error::PropertyNotFound)?;
let text_color = self.text_color.get();
if !self.selected.is_null(0)? && !self.selected.is_null(1)? {
self.render_selected(render, &rect)?;
}
let mut glyph_idx = 0;
let mut rhs = 0.;
for glyph in &*self.glyphs.lock().unwrap() {
for (glyph_idx, glyph) in self.glyphs.lock().unwrap().iter().enumerate() {
let texture = render.ctx.new_texture_from_rgba8(glyph.bmp_width, glyph.bmp_height, &glyph.bmp);
let x1 = glyph.pos.x + scroll;
@@ -150,8 +224,6 @@ impl EditBox {
render.render_box(x2, 0., x2 + CURSOR_WIDTH, rect.h, cursor_color);
}
glyph_idx += 1;
rhs = x2;
}
@@ -168,9 +240,35 @@ impl EditBox {
Ok(())
}
fn apply_cursor_scrolling(&self, rect: &Rectangle<f32>) -> Result<()> {
let cursor_pos = self.cursor_pos.get_u32(0)? as usize;
let mut scroll = self.scroll.get_f32(0)?;
pub fn render_selected<'a>(&self, render: &mut RenderContext<'a>, rect: &Rectangle<f32>) -> Result<()> {
let sel_start = self.selected.get_u32(0)? as usize;
let sel_end = self.selected.get_u32(1)? as usize;
assert!(sel_start <= sel_end);
let scroll = self.scroll.get();
let hi_bg_color = self.hi_bg_color.get();
let mut start_x = 0.;
let mut end_x = 0.;
for (glyph_idx, glyph) in self.glyphs.lock().unwrap().iter().enumerate() {
let x1 = glyph.pos.x + scroll;
let x2 = x1 + glyph.pos.w;
if glyph_idx == sel_start {
start_x = x1;
}
if glyph_idx == sel_end {
end_x = x2;
}
}
render.render_box(start_x, 0., end_x, rect.h, hi_bg_color);
Ok(())
}
fn apply_cursor_scrolling(&self, rect: &Rectangle<f32>) {
let cursor_pos = self.cursor_pos.get() as usize;
let mut scroll = self.scroll.get();
let cursor_x = {
let glyphs = &*self.glyphs.lock().unwrap();
@@ -189,13 +287,12 @@ impl EditBox {
scroll = -cursor_x;
}
self.scroll.set_f32(0, scroll)
self.scroll.set(scroll);
}
fn regen_glyphs(&self) -> Result<()> {
let glyphs = self.text_shaper.shape(self.text.get_str(0)?, self.font_size.get_f32(0)?,
[self.color.get_f32(0)?, self.color.get_f32(1)?,
self.color.get_f32(2)?, self.color.get_f32(3)?]);
let glyphs = self.text_shaper.shape(self.text.get(), self.font_size.get(),
self.text_color.get());
*self.glyphs.lock().unwrap() = glyphs;
Ok(())
}
@@ -204,6 +301,9 @@ impl EditBox {
if repeat {
return;
}
if !self.is_active.get() {
return
}
match key.as_str() {
"PageUp" => {
println!("pageup!");
@@ -212,20 +312,44 @@ impl EditBox {
println!("pagedown!");
}
"Left" => {
let cursor_pos = self.cursor_pos.get_u32(0).unwrap();
let mut cursor_pos = self.cursor_pos.get();
if cursor_pos > 0 {
self.cursor_pos.set_u32(0, cursor_pos - 1).unwrap();
cursor_pos -= 1;
self.cursor_pos.set(cursor_pos);
}
if !mods.shift {
self.selected.set_null(0).unwrap();
self.selected.set_null(1).unwrap();
} else {
if self.selected.is_null(0).unwrap() {
assert!(self.selected.is_null(1).unwrap());
self.selected.set_u32(0, cursor_pos).unwrap();
}
self.selected.set_u32(1, cursor_pos).unwrap();
}
}
"Right" => {
let cursor_pos = self.cursor_pos.get_u32(0).unwrap();
let mut cursor_pos = self.cursor_pos.get();
let glyphs_len = self.glyphs.lock().unwrap().len() as u32;
if cursor_pos < glyphs_len {
self.cursor_pos.set_u32(0, cursor_pos + 1).unwrap();
cursor_pos += 1;
self.cursor_pos.set(cursor_pos);
}
if !mods.shift {
self.selected.set_null(0).unwrap();
self.selected.set_null(1).unwrap();
} else {
if self.selected.is_null(0).unwrap() {
assert!(self.selected.is_null(1).unwrap());
self.selected.set_u32(1, cursor_pos).unwrap();
}
self.selected.set_u32(1, cursor_pos).unwrap();
}
}
"Delete" => {
let cursor_pos = self.cursor_pos.get_u32(0).unwrap();
let cursor_pos = self.cursor_pos.get();
if cursor_pos == 0 {
return;
}
@@ -240,11 +364,11 @@ impl EditBox {
}
text.push_str(&substr);
}
self.text.set_str(0, text).unwrap();
self.text.set(text);
self.regen_glyphs().unwrap();
}
"Backspace" => {
let cursor_pos = self.cursor_pos.get_u32(0).unwrap();
let cursor_pos = self.cursor_pos.get();
if cursor_pos == 0 {
return;
}
@@ -256,12 +380,49 @@ impl EditBox {
}
text.push_str(&substr);
}
self.cursor_pos.set_u32(0, cursor_pos - 1).unwrap();
self.text.set_str(0, text).unwrap();
self.cursor_pos.set(cursor_pos - 1);
self.text.set(text);
self.regen_glyphs().unwrap();
}
_ => {}
}
}
fn mouse_button_down(self: Arc<Self>, button: MouseButton, x: f32, y: f32) {
let mouse_pos = Point { x, y };
let rect = self.world_rect.lock().unwrap();
// clicking inside box will:
// 1. make it active
// 2. begin selection
if rect.contains(&mouse_pos) {
if !self.is_active.get() {
self.is_active.set(true);
println!("inside!");
// Send signal
}
// set cursor pos
// begin selection
}
// click outside the box will:
// 1. make it inactive
else {
if self.is_active.get() {
self.is_active.set(false);
// Send signal
}
}
}
fn mouse_button_up(self: Arc<Self>, button: MouseButton, x: f32, y: f32) {
// releasing mouse button will:
// 1. end selection
}
fn mouse_move(self: Arc<Self>, x: f32, y: f32) {
// if active and selection_active, then use x to modify the selection.
// also implement scrolling when cursor is to the left or right
// just scroll to the end
// also set cursor_pos too
}
}

View File

@@ -18,6 +18,7 @@ use crate::{
error::{Error, Result},
editbox,
expr::{SExprMachine, SExprVal},
keysym::MouseButtonAsU8,
prop::{Property, PropertySubType, PropertyType},
res::{ResourceId, ResourceManager},
scene::{
@@ -27,21 +28,6 @@ use crate::{
shader,
};
trait MouseButtonAsU8 {
fn to_u8(&self) -> u8;
}
impl MouseButtonAsU8 for MouseButton {
fn to_u8(&self) -> u8 {
match self {
MouseButton::Left => 0,
MouseButton::Middle => 1,
MouseButton::Right => 2,
MouseButton::Unknown => 3,
}
}
}
type Color = [f32; 4];
pub const COLOR_RED: Color = [1., 0., 0., 1.];
@@ -76,6 +62,11 @@ struct Mesh {
pub index_buffer: BufferId,
}
pub struct Point<T> {
pub x: T,
pub y: T,
}
#[derive(Debug, Clone)]
pub struct Rectangle<T: Copy + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + std::cmp::PartialOrd> {
pub x: T,
@@ -84,7 +75,7 @@ pub struct Rectangle<T: Copy + std::ops::Add<Output=T> + std::ops::Sub<Output=T>
pub h: T,
}
impl<T: Copy + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + std::cmp::PartialOrd> Rectangle<T> {
impl<T: Copy + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + std::ops::AddAssign + std::cmp::PartialOrd> Rectangle<T> {
fn from_array(arr: [T; 4]) -> Self {
let mut iter = IntoIter::new(arr);
Self {
@@ -95,7 +86,7 @@ impl<T: Copy + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + std::cmp::Par
}
}
fn clip(&self, other: &Rectangle<T>) -> Option<Rectangle<T>> {
pub fn clip(&self, other: &Self) -> Option<Self> {
if other.x + other.w < self.x ||
other.x > self.x + self.w ||
other.y + other.h < self.y ||
@@ -119,6 +110,11 @@ impl<T: Copy + std::ops::Add<Output=T> + std::ops::Sub<Output=T> + std::cmp::Par
}
Some(clipped)
}
pub fn contains(&self, point: &Point<T>) -> bool {
self.x < point.x && point.x < self.x + self.w &&
self.y < point.y && point.y < self.y + self.h
}
}
pub type FreetypeFace = ft::Face<&'static [u8]>;
@@ -472,11 +468,19 @@ impl<'a> RenderContext<'a> {
let (_, screen_height) = window::screen_size();
let mut rect = Self::get_rect(&layer)?;
rect.y = screen_height as i32 - (rect.y + rect.h);
let rect = Self::get_rect(&layer)?;
self.ctx.apply_viewport(rect.x, rect.y, rect.w, rect.h);
self.ctx.apply_scissor_rect(rect.x, rect.y, rect.w, rect.h);
let mut view = rect.clone();
view.y = screen_height as i32 - (rect.y + rect.h);
self.ctx.apply_viewport(view.x, view.y, view.w, view.h);
self.ctx.apply_scissor_rect(view.x, view.y, view.w, view.h);
let rect = Rectangle {
x: rect.x as f32,
y: rect.y as f32,
w: rect.w as f32,
h: rect.h as f32,
};
let layer_children =
layer.get_children(&[SceneNodeType::RenderMesh, SceneNodeType::RenderText, SceneNodeType::EditBox]);
@@ -536,7 +540,7 @@ impl<'a> RenderContext<'a> {
nodes.into_iter().map(|(z_index, node_inf)| node_inf).collect()
}
pub fn get_dim(mesh: &SceneNode, layer_rect: &Rectangle<i32>) -> Result<Rectangle<f32>> {
pub fn get_dim(mesh: &SceneNode, layer_rect: &Rectangle<f32>) -> Result<Rectangle<f32>> {
let prop = mesh.get_property("rect").ok_or(Error::PropertyNotFound)?;
if prop.array_len != 4 {
return Err(Error::PropertyWrongLen)
@@ -563,7 +567,7 @@ impl<'a> RenderContext<'a> {
Ok(Rectangle::from_array(rect))
}
fn render_mesh(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle<i32>) -> Result<()> {
fn render_mesh(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle<f32>) -> Result<()> {
let mesh = self.scene_graph.get_node(node_id).unwrap();
let z_index = mesh.get_property_u32("z_index")?;
@@ -754,7 +758,7 @@ impl<'a> RenderContext<'a> {
self.render_box(x1, y2 - w, x2, y2, color);
}
fn render_text(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle<i32>) -> Result<()> {
fn render_text(&mut self, node_id: SceneNodeId, layer_rect: &Rectangle<f32>) -> Result<()> {
let node = self.scene_graph.get_node(node_id).unwrap();
let text = node.get_property_str("text")?;

View File

@@ -14,6 +14,8 @@ mod expr;
mod gfx;
use gfx::run_gui;
mod keysym;
mod net;
use net::ZeroMQAdapter;

View File

@@ -13,6 +13,11 @@ use std::{
use crate::{expr::SExprCode, scene::SceneNodeId};
mod wrap;
pub use wrap::{
PropertyBool, PropertyFloat32, PropertyUint32, PropertyStr, PropertyColor,
};
type Buffer = Arc<Vec<u8>>;
#[derive(Debug, Copy, Clone, PartialEq, SerialEncodable, SerialDecodable)]
@@ -437,7 +442,7 @@ impl Property {
// Get
fn is_bounded(&self) -> bool {
pub fn is_bounded(&self) -> bool {
self.array_len != 0
}
@@ -454,6 +459,10 @@ impl Property {
let val = self.get_raw_value(i)?;
Ok(val.is_unset())
}
pub fn is_null(&self, i: usize) -> Result<bool> {
let val = self.get_raw_value(i)?;
Ok(val.is_null())
}
pub fn is_expr(&self, i: usize) -> Result<bool> {
if !self.is_expr_allowed {

View File

@@ -0,0 +1,134 @@
use std::sync::Arc;
use crate::{scene::SceneNode, error::{Error, Result}};
use super::Property;
pub struct PropertyBool {
prop: Arc<Property>,
idx: usize,
}
impl PropertyBool {
pub fn wrap(node: &SceneNode, prop_name: &str, idx: usize) -> Result<Self> {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
// Test if it works
let _ = prop.get_bool(idx)?;
Ok(Self { prop, idx })
}
pub fn get(&self) -> bool {
self.prop.get_bool(self.idx).unwrap()
}
pub fn set(&self, val: bool) {
self.prop.set_bool(self.idx, val).unwrap()
}
}
pub struct PropertyUint32 {
prop: Arc<Property>,
idx: usize,
}
impl PropertyUint32 {
pub fn wrap(node: &SceneNode, prop_name: &str, idx: usize) -> Result<Self> {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
// Test if it works
let _ = prop.get_u32(idx)?;
Ok(Self { prop, idx })
}
pub fn get(&self) -> u32 {
self.prop.get_u32(self.idx).unwrap()
}
pub fn set(&self, val: u32) {
self.prop.set_u32(self.idx, val).unwrap()
}
}
pub struct PropertyFloat32 {
prop: Arc<Property>,
idx: usize,
}
impl PropertyFloat32 {
pub fn wrap(node: &SceneNode, prop_name: &str, idx: usize) -> Result<Self> {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
// Test if it works
let _ = prop.get_f32(idx)?;
Ok(Self { prop, idx })
}
pub fn get(&self) -> f32 {
self.prop.get_f32(self.idx).unwrap()
}
pub fn set(&self, val: f32) {
self.prop.set_f32(self.idx, val).unwrap()
}
}
pub struct PropertyStr {
prop: Arc<Property>,
idx: usize,
}
impl PropertyStr {
pub fn wrap(node: &SceneNode, prop_name: &str, idx: usize) -> Result<Self> {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
// Test if it works
let _ = prop.get_str(idx)?;
Ok(Self { prop, idx })
}
pub fn get(&self) -> String {
self.prop.get_str(self.idx).unwrap()
}
pub fn set<S: Into<String>>(&self, val: S) {
self.prop.set_str(self.idx, val.into()).unwrap()
}
}
pub struct PropertyColor {
prop: Arc<Property>,
}
impl PropertyColor {
pub fn wrap(node: &SceneNode, prop_name: &str) -> Result<Self> {
let prop = node.get_property(prop_name).ok_or(Error::PropertyNotFound)?;
if !prop.is_bounded() || prop.get_len() != 4 {
return Err(Error::PropertyWrongLen)
}
// Test if it works
let _ = prop.get_f32(0)?;
Ok(Self { prop })
}
pub fn get(&self) -> [f32; 4] {
[self.prop.get_f32(0).unwrap(),
self.prop.get_f32(1).unwrap(),
self.prop.get_f32(2).unwrap(),
self.prop.get_f32(3).unwrap()]
}
pub fn set(&self, val: [f32; 4]) {
self.prop.set_f32(0, val[0]).unwrap();
self.prop.set_f32(1, val[1]).unwrap();
self.prop.set_f32(2, val[2]).unwrap();
self.prop.set_f32(3, val[3]).unwrap();
}
}