mirror of
https://github.com/danbulant/mangui
synced 2026-06-19 22:31:03 +00:00
add scrolling capability
This commit is contained in:
parent
36065e3221
commit
8875cbb6ff
7 changed files with 240 additions and 96 deletions
|
|
@ -5,74 +5,74 @@ use mangui::nodes::image::ImageLoad;
|
|||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct GraphqlResponse<T> {
|
||||
data: T
|
||||
pub(crate) data: T
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct MediaListCollectionData {
|
||||
#[serde(rename = "MediaListCollection")]
|
||||
media_list_collection: MediaListCollection
|
||||
pub(crate) media_list_collection: MediaListCollection
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct MediaListCollection {
|
||||
lists: Vec<MediaList>
|
||||
pub(crate) lists: Vec<MediaList>
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct MediaList {
|
||||
name: String,
|
||||
pub(crate) struct MediaList {
|
||||
pub(crate) name: String,
|
||||
#[serde(rename = "isCustomList")]
|
||||
is_custom_list: bool,
|
||||
status: String,
|
||||
pub(crate) is_custom_list: bool,
|
||||
pub(crate) status: String,
|
||||
#[serde(rename = "isSplitCompletedList")]
|
||||
is_split_completed_list: bool,
|
||||
entries: Vec<MediaListEntry>,
|
||||
pub(crate) is_split_completed_list: bool,
|
||||
pub(crate) entries: Vec<MediaListEntry>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct MediaListEntry {
|
||||
status: String,
|
||||
progress: i32,
|
||||
pub(crate) struct MediaListEntry {
|
||||
pub(crate) status: String,
|
||||
pub(crate) progress: i32,
|
||||
#[serde(rename = "progressVolumes")]
|
||||
progress_volumes: i32,
|
||||
repeat: i32,
|
||||
priority: i32,
|
||||
private: bool,
|
||||
notes: Option<String>,
|
||||
score: f32,
|
||||
media: MediaEntry,
|
||||
pub(crate) progress_volumes: i32,
|
||||
pub(crate) repeat: i32,
|
||||
pub(crate) priority: i32,
|
||||
pub(crate) private: bool,
|
||||
pub(crate) notes: Option<String>,
|
||||
pub(crate) score: f32,
|
||||
pub(crate) media: MediaEntry,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct MediaEntry {
|
||||
id: i32,
|
||||
title: MediaTitle,
|
||||
status: String,
|
||||
chapters: Option<i32>,
|
||||
volumes: Option<i32>,
|
||||
pub(crate) struct MediaEntry {
|
||||
pub(crate) id: i32,
|
||||
pub(crate) title: MediaTitle,
|
||||
pub(crate) status: String,
|
||||
pub(crate) chapters: Option<i32>,
|
||||
pub(crate) volumes: Option<i32>,
|
||||
#[serde(rename = "coverImage")]
|
||||
cover_image: CoverImage,
|
||||
pub(crate) cover_image: CoverImage,
|
||||
#[serde(rename = "isAdult")]
|
||||
is_adult: bool,
|
||||
pub(crate) is_adult: bool,
|
||||
#[serde(rename = "isFavourite")]
|
||||
is_favourite: bool,
|
||||
pub(crate) is_favourite: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct MediaTitle {
|
||||
romaji: String,
|
||||
english: Option<String>,
|
||||
native: String,
|
||||
pub(crate) struct MediaTitle {
|
||||
pub(crate) romaji: String,
|
||||
pub(crate) english: Option<String>,
|
||||
pub(crate) native: String,
|
||||
#[serde(rename = "userPreferred")]
|
||||
user_preferred: String,
|
||||
pub(crate) user_preferred: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct CoverImage {
|
||||
large: String,
|
||||
medium: String,
|
||||
color: Option<String>,
|
||||
pub(crate) struct CoverImage {
|
||||
pub(crate) large: String,
|
||||
pub(crate) medium: String,
|
||||
pub(crate) color: Option<String>,
|
||||
}
|
||||
|
||||
// pub fn load_demo() -> MediaListCollection {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
use std::sync::{Arc, mpsc, Mutex};
|
||||
use mangui::nodes::layout::Layout;
|
||||
use mangui::{MainEntry, SharedNode};
|
||||
use mangui::femtovg::Paint;
|
||||
use mangui::dpi::PhysicalPosition;
|
||||
use mangui::events::InnerEvent;
|
||||
use mangui::femtovg::{ImageFlags, Paint};
|
||||
use mangui::nodes::text::Text;
|
||||
use mangui::nodes::{Style, TaffyStyle, ToShared};
|
||||
use mangui::nodes::image::{Image, ImageLoad};
|
||||
use mangui::taffy::{AlignItems, FlexDirection, JustifyContent, LengthPercentage, LengthPercentageAuto, Point, Rect};
|
||||
use uno_gen::uno;
|
||||
use crate::anilist::load_demo_async;
|
||||
|
|
@ -45,8 +48,8 @@ async fn main() {
|
|||
let groot_clone = groot.clone();
|
||||
tokio::spawn(async move {
|
||||
let data = load_demo_async().await;
|
||||
|
||||
let mainview_container = Layout::default()
|
||||
|
||||
let mut mainview_container = Layout::default()
|
||||
.style(Style {
|
||||
layout: TaffyStyle {
|
||||
flex_grow: 1.,
|
||||
|
|
@ -56,7 +59,26 @@ async fn main() {
|
|||
background: Some(Paint::color(*tokens::BACKGROUND)),
|
||||
..Default::default()
|
||||
})
|
||||
.to_shared();
|
||||
.to_arcmutex();
|
||||
mainview_container.lock().unwrap().events.add_handler(Box::new({
|
||||
let mainview_container = mainview_container.clone();
|
||||
move |event| {
|
||||
if let InnerEvent::Wheel { delta, .. } = event.event {
|
||||
let delta = match delta {
|
||||
mangui::events::MouseScrollDelta::LineDelta(_, y) => y * 30f32,
|
||||
mangui::events::MouseScrollDelta::PixelDelta(PhysicalPosition { y, .. }) => y as f32,
|
||||
};
|
||||
let mut layout = mainview_container.lock().unwrap();
|
||||
// layout.style.layout.scroll.y -= delta * 10.;
|
||||
layout.style.scroll_y -= delta;
|
||||
// cap scroll_y to 0
|
||||
if layout.style.scroll_y < 0. {
|
||||
layout.style.scroll_y = 0.;
|
||||
}
|
||||
println!("scroll_y: {}", layout.style.scroll_y);
|
||||
}
|
||||
}
|
||||
}));
|
||||
let i = LengthPercentageAuto::Length(5.);
|
||||
let title = Text::new("Mangades".to_owned(), TEXT_LARGE)
|
||||
.style(Style {
|
||||
|
|
@ -64,16 +86,76 @@ async fn main() {
|
|||
..uno!(p-10)
|
||||
})
|
||||
.to_shared();
|
||||
append(&{ mainview_container.clone() }, &title);
|
||||
|
||||
append(&mainview_container, &title);
|
||||
for list in data.lists {
|
||||
let list_container = Layout::default()
|
||||
.style(Style {
|
||||
layout: TaffyStyle {
|
||||
flex_grow: 1.,
|
||||
flex_direction: FlexDirection::Column,
|
||||
..Default::default()
|
||||
},
|
||||
background: Some(Paint::color(*tokens::BACKGROUND)),
|
||||
..Default::default()
|
||||
})
|
||||
.to_shared();
|
||||
let list_title = Text::new(list.name, TEXT_LARGE)
|
||||
.style(Style {
|
||||
text_fill: Some(Paint::color(*tokens::WHITE)),
|
||||
..uno!(p-10)
|
||||
})
|
||||
.to_shared();
|
||||
append(&{ mainview_container.clone() }, &list_container);
|
||||
append(&list_container, &list_title);
|
||||
|
||||
for entry in list.entries {
|
||||
let entry_container = Layout::default()
|
||||
.style(Style {
|
||||
layout: TaffyStyle {
|
||||
flex_grow: 1.,
|
||||
flex_direction: FlexDirection::Row,
|
||||
..Default::default()
|
||||
},
|
||||
background: Some(Paint::color(*tokens::BACKGROUND)),
|
||||
..Default::default()
|
||||
})
|
||||
.to_shared();
|
||||
// image loading disabled for speed
|
||||
// let addr = entry.media.cover_image.large;
|
||||
// // use only last two parts from url, which is a folder and a file (either medium/something.jpg or large/something.jpg)
|
||||
// let addr = addr.split('/').collect::<Vec<&str>>().into_iter().rev().take(2).collect::<Vec<&str>>().into_iter().rev().collect::<Vec<&str>>().join("/");
|
||||
// let addr = addr.replace("medium", "large").replace("small", "medium");
|
||||
// let addr = format!("demo/{}", addr);
|
||||
// dbg!(&addr);
|
||||
// let image = Image::new(
|
||||
// ImageLoad::LoadFile(addr.parse().unwrap(),
|
||||
// ImageFlags::empty())
|
||||
// )
|
||||
// .style(Style {
|
||||
// ..Default::default()
|
||||
// })
|
||||
// .to_shared();
|
||||
let title = Text::new(entry.media.title.user_preferred, TEXT_LARGE)
|
||||
.style(Style {
|
||||
text_fill: Some(Paint::color(*tokens::WHITE)),
|
||||
..uno!(p-10)
|
||||
})
|
||||
.to_shared();
|
||||
append(&list_container, &entry_container);
|
||||
// append(&entry_container, &image);
|
||||
append(&entry_container, &title);
|
||||
}
|
||||
}
|
||||
|
||||
detach(&loading_container);
|
||||
append(&groot_clone, &mainview_container);
|
||||
|
||||
append(&groot_clone, &{ mainview_container.clone() });
|
||||
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
mangui::run_event_loop(MainEntry {
|
||||
root: groot.clone(),
|
||||
render: rx
|
||||
});
|
||||
}).unwrap();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,4 +8,4 @@ edition = "2021"
|
|||
[dependencies]
|
||||
mangui = { path = "../ui"}
|
||||
rusalka = { path = "../rusalka"}
|
||||
rusalka-macro = { path = "../rusalka-macro"}
|
||||
rusalka-macro = { path = "../rusalka-macro"}
|
||||
113
ui/src/lib.rs
113
ui/src/lib.rs
|
|
@ -35,6 +35,7 @@ pub mod events;
|
|||
pub use taffy;
|
||||
pub use femtovg;
|
||||
pub use cosmic_text;
|
||||
pub use winit::dpi;
|
||||
|
||||
pub type CurrentRenderer = OpenGl;
|
||||
pub type SharedNode = Arc<Mutex<dyn Node>>;
|
||||
|
|
@ -64,7 +65,7 @@ pub struct MainEntry {
|
|||
/// The event loop only returns when the window is closed, and all the resources regarding the window are freed.
|
||||
/// Note that the DOM tree may not be destroyed if you hold a reference to it, and the DOM tree can be used again, although it's discouraged -
|
||||
/// your app should exit at this point and only do cleanup.
|
||||
pub fn run_event_loop(entry: MainEntry) -> () {
|
||||
pub fn run_event_loop(entry: MainEntry) -> Result<(), winit::error::EventLoopError> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
let (buffer_context, gl_display, window, surface) = create_window(&event_loop);
|
||||
|
||||
|
|
@ -107,9 +108,47 @@ pub fn run_event_loop(entry: MainEntry) -> () {
|
|||
let focus_path: Option<Vec<WeakNode>> = None;
|
||||
let mut mouse_values: HashMap<DeviceId, MouseValue> = HashMap::new();
|
||||
|
||||
event_loop.run(move |event, target| match event {
|
||||
let res = event_loop.run(move |event, target| match event {
|
||||
Event::WindowEvent { event, .. } => match event {
|
||||
WindowEvent::MouseWheel { device_id: _, delta: _, phase: _, .. } => {},
|
||||
WindowEvent::MouseWheel { device_id, delta, phase } => {
|
||||
let default = MouseValue {
|
||||
last_location: Location::new(0., 0.),
|
||||
buttons: 0
|
||||
};
|
||||
let mouse_value = mouse_values.get(&device_id)
|
||||
.unwrap_or(&default);
|
||||
|
||||
let path = get_element_at(&root, &context, mouse_value.last_location);
|
||||
|
||||
if let Some(path) = path {
|
||||
let target_layout = context.node_layout.get(path.last().unwrap());
|
||||
let target_layout = match target_layout {
|
||||
Some(target_layout) => target_layout,
|
||||
None => { return; }
|
||||
};
|
||||
let target_layout = context.taffy.layout(target_layout.to_owned()).unwrap();
|
||||
let event = NodeEvent {
|
||||
target: path.last().unwrap().clone(),
|
||||
path: path.clone(),
|
||||
event: events::InnerEvent::Wheel {
|
||||
delta,
|
||||
phase,
|
||||
mouse: MouseEvent {
|
||||
button: None,
|
||||
buttons: mouse_value.buttons,
|
||||
client: mouse_value.last_location,
|
||||
movement: Location::new(0., 0.),
|
||||
device: device_id,
|
||||
modifiers,
|
||||
offset: mouse_value.last_location - target_layout.location.into()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
run_event_handlers(path, event);
|
||||
window.request_redraw();
|
||||
}
|
||||
},
|
||||
WindowEvent::CursorMoved { device_id, position, .. } => {
|
||||
let mouse_value = mouse_values.get(&device_id);
|
||||
let (movement, location, mouse_value) = match mouse_value {
|
||||
|
|
@ -168,9 +207,9 @@ pub fn run_event_loop(entry: MainEntry) -> () {
|
|||
match &focus_path {
|
||||
Some(path) => {
|
||||
let strong_focus_path: Option<Vec<SharedNode>> = convert_vec_option_to_option_vec(path.iter().map(|weak| weak.upgrade()).collect());
|
||||
if matches!(strong_focus_path, None) { return; }
|
||||
if strong_focus_path.is_none() { return; }
|
||||
let strong_focus_path = strong_focus_path.unwrap();
|
||||
if strong_focus_path.len() == 0 { return; }
|
||||
if strong_focus_path.is_empty() { return; }
|
||||
|
||||
let focus_event = NodeEvent {
|
||||
target: strong_focus_path.last().unwrap().clone(),
|
||||
|
|
@ -197,7 +236,7 @@ pub fn run_event_loop(entry: MainEntry) -> () {
|
|||
WindowEvent::MouseInput { device_id, state, button, .. } => {
|
||||
let mouse_value = mouse_values.get(&device_id);
|
||||
let mut mouse_value = match mouse_value {
|
||||
Some(mouse_value) => mouse_value.clone(),
|
||||
Some(mouse_value) => *mouse_value,
|
||||
None => { return; } // Mouse move should be fired first
|
||||
};
|
||||
mouse_value.update_buttons(button, state);
|
||||
|
|
@ -205,36 +244,33 @@ pub fn run_event_loop(entry: MainEntry) -> () {
|
|||
let location = mouse_value.last_location;
|
||||
let path = get_element_at(&root, &context, location);
|
||||
|
||||
match path {
|
||||
Some(path) => {
|
||||
let target_layout = context.node_layout.get(path.last().unwrap());
|
||||
let target_layout = match target_layout {
|
||||
Some(target_layout) => target_layout,
|
||||
None => { return; }
|
||||
};
|
||||
let target_layout = context.taffy.layout(target_layout.to_owned()).unwrap();
|
||||
let mevent = MouseEvent {
|
||||
button: Some(button),
|
||||
buttons: mouse_value.buttons,
|
||||
client: location,
|
||||
movement: Location::new(0., 0.),
|
||||
device: device_id,
|
||||
modifiers,
|
||||
offset: location - target_layout.location.into()
|
||||
};
|
||||
let event = NodeEvent {
|
||||
target: path.last().unwrap().clone(),
|
||||
path: path.clone(),
|
||||
event: match state {
|
||||
winit::event::ElementState::Pressed => events::InnerEvent::MouseDown(mevent),
|
||||
winit::event::ElementState::Released => events::InnerEvent::MouseUp(mevent)
|
||||
}
|
||||
};
|
||||
if let Some(path) = path {
|
||||
let target_layout = context.node_layout.get(path.last().unwrap());
|
||||
let target_layout = match target_layout {
|
||||
Some(target_layout) => target_layout,
|
||||
None => { return; }
|
||||
};
|
||||
let target_layout = context.taffy.layout(target_layout.to_owned()).unwrap();
|
||||
let mevent = MouseEvent {
|
||||
button: Some(button),
|
||||
buttons: mouse_value.buttons,
|
||||
client: location,
|
||||
movement: Location::new(0., 0.),
|
||||
device: device_id,
|
||||
modifiers,
|
||||
offset: location - target_layout.location.into()
|
||||
};
|
||||
let event = NodeEvent {
|
||||
target: path.last().unwrap().clone(),
|
||||
path: path.clone(),
|
||||
event: match state {
|
||||
winit::event::ElementState::Pressed => events::InnerEvent::MouseDown(mevent),
|
||||
winit::event::ElementState::Released => events::InnerEvent::MouseUp(mevent)
|
||||
}
|
||||
};
|
||||
|
||||
window.request_redraw();
|
||||
run_event_handlers(path, event);
|
||||
},
|
||||
None => {}
|
||||
window.request_redraw();
|
||||
run_event_handlers(path, event);
|
||||
}
|
||||
},
|
||||
WindowEvent::CloseRequested => target.exit(),
|
||||
|
|
@ -299,7 +335,7 @@ pub fn run_event_loop(entry: MainEntry) -> () {
|
|||
// dbg!("recomputed");
|
||||
}
|
||||
// Clear the render queue
|
||||
while let Ok(_) = entry.render.try_recv() {}
|
||||
while entry.render.try_recv().is_ok() {}
|
||||
render(&buffer_context, &surface, &window, &mut context, &root);
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -310,7 +346,7 @@ pub fn run_event_loop(entry: MainEntry) -> () {
|
|||
// dbg!(refresh_rate);
|
||||
// some leeway before vsync
|
||||
// target.set_control_flow(ControlFlow::wait_duration(Duration::from_millis(1000 / refresh_rate as u64 - 100/refresh_rate as u64)));
|
||||
if let Ok(_) = entry.render.try_recv() {
|
||||
if entry.render.try_recv().is_ok() {
|
||||
window.request_redraw();
|
||||
}
|
||||
// }
|
||||
|
|
@ -318,7 +354,8 @@ pub fn run_event_loop(entry: MainEntry) -> () {
|
|||
},
|
||||
// In the future, window should be created after resuming from suspend (for android support)
|
||||
_ => {}
|
||||
}).unwrap();
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
/// I have no idea if there's a better way to do this in rust...
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
use std::fmt::{Debug, Formatter};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use femtovg::{Paint, Path};
|
||||
use crate::{nodes::{Node, NodeChildren, Style}, events::handler::EventHandlerDatabase, WeakNode, SharedNode};
|
||||
use taffy::style::Dimension;
|
||||
use crate::nodes::primitives::draw_rect;
|
||||
|
|
|
|||
|
|
@ -77,7 +77,15 @@ pub struct Style {
|
|||
/// border radius in pixels
|
||||
pub border_radius: f32,
|
||||
/// Various transformation (position, scale and rotation)
|
||||
pub transform: Option<Transform>
|
||||
pub transform: Option<Transform>,
|
||||
/// sets scroll offset for x-axis
|
||||
/// 0.0 is the default value
|
||||
/// you cannot scroll outside the layout - render function will clip the value in that case
|
||||
pub scroll_x: f32,
|
||||
/// sets scroll offset for y-axis
|
||||
/// 0.0 is the default value
|
||||
/// you cannot scroll outside the layout - render function will clip the value in that case
|
||||
pub scroll_y: f32,
|
||||
}
|
||||
|
||||
type NodeChildren = Vec<SharedNode>;
|
||||
|
|
@ -295,6 +303,9 @@ pub trait Node: Debug + Send {
|
|||
|
||||
pub trait ToShared {
|
||||
fn to_shared(self) -> SharedNode;
|
||||
fn to_arcmutex(self) -> Arc<Mutex<Self>> where Self: Sized {
|
||||
Arc::new(Mutex::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Node + 'static> ToShared for T {
|
||||
|
|
@ -397,13 +408,20 @@ pub(crate) fn render_recursively(node: &SharedNode, context: &mut RenderContext)
|
|||
let sself = node.clone();
|
||||
context.canvas.save();
|
||||
let offset = styles.transform.as_ref().map(|t| (t.position.x, t.position.y)).unwrap_or((0., 0.));
|
||||
context.canvas.translate(layout.location.x + offset.0, layout.location.y + offset.1);
|
||||
let scroll_offset = (styles.scroll_x, styles.scroll_y);
|
||||
let content_size = layout.content_size;
|
||||
let visible_size = layout.size;
|
||||
let scroll_offset = (scroll_offset.0.min(content_size.width - visible_size.width).max(0.), scroll_offset.1.min(content_size.height - visible_size.height).max(0.));
|
||||
context.canvas.translate(
|
||||
layout.location.x + offset.0 - scroll_offset.0,
|
||||
layout.location.y + offset.1 - scroll_offset.1
|
||||
);
|
||||
if let Some(transform) = &styles.transform {
|
||||
context.canvas.scale(transform.scale.width, transform.scale.height);
|
||||
context.canvas.rotate(transform.rotation);
|
||||
}
|
||||
let clip_width = matches!(styles.layout.overflow.x, Overflow::Hidden | Overflow::Clip);
|
||||
let clip_height = matches!(styles.layout.overflow.y, Overflow::Hidden | Overflow::Clip);
|
||||
let clip_width = matches!(styles.layout.overflow.x, Overflow::Hidden | Overflow::Clip | Overflow::Scroll);
|
||||
let clip_height = matches!(styles.layout.overflow.y, Overflow::Hidden | Overflow::Clip | Overflow::Scroll);
|
||||
if clip_width || clip_height {
|
||||
context.canvas.scissor(
|
||||
0.,
|
||||
|
|
@ -413,13 +431,14 @@ pub(crate) fn render_recursively(node: &SharedNode, context: &mut RenderContext)
|
|||
);
|
||||
}
|
||||
drop(read_node);
|
||||
sself.lock().unwrap().render_pre_children(context, layout);
|
||||
if let Some(children) = sself.lock().unwrap().children() {
|
||||
let mut locked = sself.lock().unwrap();
|
||||
locked.render_pre_children(context, layout);
|
||||
if let Some(children) = locked.children() {
|
||||
for child in children {
|
||||
render_recursively(child, context);
|
||||
}
|
||||
}
|
||||
sself.lock().unwrap().render_post_children(context, layout);
|
||||
locked.render_post_children(context, layout);
|
||||
context.canvas.restore();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ macro_rules! impl_enum_totokens {
|
|||
)?
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ macro_rules! impl_struct_usersettable_totokens {
|
|||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ impl<T> UserSettable<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
match self {
|
||||
UserSettable::Value(_) => false,
|
||||
|
|
@ -831,6 +831,14 @@ fn process_rules(rules: Vec<Rule>) -> Result<Style, RuleParseError> {
|
|||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.layout.require_non_arbitrary()?.overflow.require_non_arbitrary()?.y = value;
|
||||
},
|
||||
"rounded" => {
|
||||
if let Some(value) = value {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
style.border_radius = value;
|
||||
} else {
|
||||
style.border_radius = UserSettable::Value(8.);
|
||||
}
|
||||
},
|
||||
"layout" => {
|
||||
if let Some(value) = value {
|
||||
let value = value.to_user_settable(name_span, inverse)?;
|
||||
|
|
|
|||
Loading…
Reference in a new issue