Skip to content

Commit

Permalink
Merge branch 'main' into feat/dioxus-0.6
Browse files Browse the repository at this point in the history
  • Loading branch information
marc2332 authored Dec 21, 2024
2 parents efd284c + 10673cc commit 7ac127b
Show file tree
Hide file tree
Showing 38 changed files with 4,721 additions and 2,761 deletions.
163 changes: 163 additions & 0 deletions crates/components/src/animated_position.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
use std::time::Duration;

use dioxus::prelude::*;
use freya_elements::elements as dioxus_elements;
use freya_hooks::{
use_animation_with_dependencies,
use_node_signal_with_prev,
AnimDirection,
AnimNum,
Ease,
Function,
};

#[component]
pub fn AnimatedPosition(
children: Element,
width: String,
height: String,
#[props(default = Function::default())] function: Function,
#[props(default = Duration::from_millis(250))] duration: Duration,
#[props(default = Ease::default())] ease: Ease,
) -> Element {
let mut render_element = use_signal(|| false);
let (reference, size, old_size) = use_node_signal_with_prev();

let animations = use_animation_with_dependencies(
&(function, duration, ease),
move |ctx, (function, duration, ease)| {
let old_size = old_size().unwrap_or_default();
let size = size().unwrap_or_default();
(
ctx.with(
AnimNum::new(size.area.origin.x, old_size.area.origin.x)
.duration(duration)
.ease(ease)
.function(function),
),
ctx.with(
AnimNum::new(size.area.origin.y, old_size.area.origin.y)
.duration(duration)
.ease(ease)
.function(function),
),
)
},
);

use_effect(move || {
if animations.is_running() {
render_element.set(true);
}
});

use_effect(move || {
let has_size = size.read().is_some();
let has_old_size = old_size.read().is_some();
if has_size && has_old_size {
animations.run(AnimDirection::Reverse);
} else if has_size {
render_element.set(true);
}
});

let (offset_x, offset_y) = animations.get();
let offset_x = offset_x.read().as_f32();
let offset_y = offset_y.read().as_f32();

rsx!(
rect {
reference,
width: "{width}",
height: "{height}",
rect {
width: "0",
height: "0",
offset_x: "{offset_x}",
offset_y: "{offset_y}",
position: "global",
if render_element() {
rect {
width: "{size.read().as_ref().unwrap().area.width()}",
height: "{size.read().as_ref().unwrap().area.height()}",
{children}
}
}
}
}
)
}

#[cfg(test)]
mod test {
use std::time::Duration;

use freya::prelude::*;
use freya_testing::prelude::*;

#[tokio::test]
pub async fn animated_position() {
fn animated_position_app() -> Element {
let mut padding = use_signal(|| (100., 100.));

rsx!(
rect {
padding: "{padding().0} {padding().1}",
onclick: move |_| {
padding.write().0 += 10.;
padding.write().1 += 10.;
},
AnimatedPosition {
width: "50",
height: "50",
function: Function::Linear
}
}
)
}

let mut utils = launch_test(animated_position_app);

// Disable event loop ticker
utils.config().event_loop_ticker = false;

let root = utils.root();
utils.wait_for_update().await;
utils.wait_for_update().await;

let get_positions = || {
root.get(0)
.get(0)
.get(0)
.get(0)
.layout()
.unwrap()
.area
.origin
};

assert_eq!(get_positions().x, 100.);
assert_eq!(get_positions().y, 100.);

utils.click_cursor((5.0, 5.0)).await;
utils.wait_for_update().await;
utils.wait_for_update().await;
tokio::time::sleep(Duration::from_millis(125)).await;
utils.wait_for_update().await;
utils.wait_for_update().await;

assert!(get_positions().x < 106.);
assert!(get_positions().x > 105.);

assert!(get_positions().y < 106.);
assert!(get_positions().y > 105.);

utils.config().event_loop_ticker = true;

utils.wait_for_update().await;
tokio::time::sleep(Duration::from_millis(125)).await;
utils.wait_for_update().await;

assert_eq!(get_positions().x, 110.);
}
}
11 changes: 10 additions & 1 deletion crates/components/src/drag_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,21 @@ pub struct DropZoneProps<T: 'static + PartialEq + Clone> {
children: Element,
/// Handler for the `ondrop` event.
ondrop: EventHandler<T>,
/// Width of the [DropZone].
#[props(default = "auto".to_string())]
width: String,
/// Height of the [DropZone].
#[props(default = "auto".to_string())]
height: String,
}

/// Elements from [`DragZone`]s can be dropped here.
#[allow(non_snake_case)]
pub fn DropZone<T: 'static + Clone + PartialEq>(props: DropZoneProps<T>) -> Element {
let mut drags = use_context::<Signal<Option<T>>>();

let onmouseup = move |_: MouseEvent| {
let onmouseup = move |e: MouseEvent| {
e.stop_propagation();
if let Some(current_drags) = &*drags.read() {
props.ondrop.call(current_drags.clone());
}
Expand All @@ -134,6 +141,8 @@ pub fn DropZone<T: 'static + Clone + PartialEq>(props: DropZoneProps<T>) -> Elem
rsx!(
rect {
onmouseup,
width: props.width,
height: props.height,
{props.children}
}
)
Expand Down
6 changes: 6 additions & 0 deletions crates/components/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod accordion;
mod activable_route;
mod animated_position;
mod animated_router;
mod body;
mod button;
Expand All @@ -23,9 +24,11 @@ mod menu;
mod native_container;
mod native_router;
mod network_image;
mod overflowed_content;
mod popup;
mod progress_bar;
mod radio;
mod resizable_container;
mod scroll_views;
mod sidebar;
mod slider;
Expand All @@ -42,6 +45,7 @@ mod window_drag_area;

pub use accordion::*;
pub use activable_route::*;
pub use animated_position::*;
pub use animated_router::*;
pub use body::*;
pub use button::*;
Expand All @@ -61,9 +65,11 @@ pub use menu::*;
pub use native_container::*;
pub use native_router::*;
pub use network_image::*;
pub use overflowed_content::*;
pub use popup::*;
pub use progress_bar::*;
pub use radio::*;
pub use resizable_container::*;
pub use scroll_views::*;
pub use sidebar::*;
pub use slider::*;
Expand Down
137 changes: 137 additions & 0 deletions crates/components/src/overflowed_content.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use std::time::Duration;

use dioxus::prelude::*;
use freya_elements::elements as dioxus_elements;
use freya_hooks::{
use_animation,
use_node_signal,
AnimDirection,
AnimNum,
Ease,
Function,
OnFinish,
};

/// Animate the content of a container when the content overflows.
///
/// This is primarily targeted to text that can't be fully shown in small layouts.
///
/// # Example
///
/// ```no_run
/// # use freya::prelude::*;
/// fn app() -> Element {
/// rsx!(
/// Button {
/// OverflowedContent {
/// width: "100",
/// rect {
/// direction: "horizontal",
/// cross_align: "center",
/// label {
/// "Freya is a cross-platform GUI library for Rust"
/// }
/// }
/// }
/// }
/// )
/// }
/// ```
#[component]
pub fn OverflowedContent(
children: Element,
#[props(default = "100%".to_string())] width: String,
#[props(default = "auto".to_string())] height: String,
#[props(default = Duration::from_secs(4))] duration: Duration,
) -> Element {
let (label_reference, label_size) = use_node_signal();
let (rect_reference, rect_size) = use_node_signal();

let rect_width = rect_size.read().area.width();
let label_width = label_size.read().area.width();
let does_overflow = label_width > rect_width;

let animations = use_animation(move |ctx| {
ctx.on_finish(OnFinish::Restart);

ctx.with(
AnimNum::new(0., 100.)
.duration(duration)
.ease(Ease::InOut)
.function(Function::Linear),
)
});

use_effect(use_reactive!(|does_overflow| {
if does_overflow {
animations.run(AnimDirection::Forward);
}
}));

let progress = animations.get();
let progress = progress.read().as_f32();
let offset_x = if does_overflow {
((label_width + rect_width) * progress / 100.) - rect_width
} else {
0.
};

rsx!(
rect {
width,
height,
offset_x: "{-offset_x}",
overflow: "clip",
reference: rect_reference,
rect {
reference: label_reference,
max_lines: "1",
{children}
}
}
)
}

#[cfg(test)]
mod test {
use std::time::Duration;

use freya::prelude::*;
use freya_testing::prelude::*;
use tokio::time::sleep;

#[tokio::test]
pub async fn overflowed_content() {
fn app() -> Element {
rsx!(
OverflowedContent {
duration: Duration::from_millis(50),
width: "50",
label {
"123456789123456789"
}
}
)
}

let mut utils = launch_test(app);

let root = utils.root();
let label = root.get(0).get(0).get(0);

utils.wait_for_update().await;
utils.wait_for_update().await;
assert_eq!(label.layout().unwrap().area.min_x(), 50.);

sleep(Duration::from_millis(50)).await;
utils.wait_for_update().await;
utils.wait_for_update().await;
assert!(label.layout().unwrap().area.min_x() < 0.);

sleep(Duration::from_millis(50)).await;
utils.wait_for_update().await;
utils.wait_for_update().await;
utils.wait_for_update().await;
assert_eq!(label.layout().unwrap().area.min_x(), 50.);
}
}
Loading

0 comments on commit 7ac127b

Please sign in to comment.