Compare commits

...
32 changed files with 601 additions and 267 deletions

No files matched your search

+2 -1
View File
@@ -9,8 +9,9 @@
gap="8"
round="8"/>
<!-- id -->
<template name="GroupBox">
<rectangle macro="group_box">
<rectangle macro="group_box" id="rect">
<CHILDREN/>
</rectangle>
</template>
@@ -3,7 +3,7 @@
<!-- device_name, device_icon -->
<template name="DeviceSlider">
<GroupBox>
<GroupBox id="rect">
<div width="100%" align_items="center" justify_content="center" gap="8">
<sprite src_builtin="${device_icon}" width="16" height="16" color="on_background" />
<label text="${device_name}" margin_right="8" size="12" weight="bold" color="on_background" />
@@ -47,8 +47,17 @@
<elements>
<div flex_direction="column" gap="4">
<div id="devices" flex_direction="column" gap="4" max_height="400" overflow_y="scroll">
<!--
this div shouldn't be technically needed, but there's a bug in
the newest taffy version (or we're doing something wrong),
causing the scrollbar to show up, even if it's not overflowing.
OVERFLOW_START_THRESHOLD_UNITS is already set to 3, but it's not
sufficient enough in this case?? (margin_bottom="4" fixes that).
-->
<div width="100%" flex_direction="column" max_height="400" overflow_y="scroll">
<div id="devices" width="100%" flex_direction="column" gap="4" margin_bottom="4">
</div>
</div>
<!-- bottom buttons -->
@@ -20,7 +20,7 @@
</template>
<elements>
<div flex_direction="column" gap="8" width="100%" align_items="center">
<div flex_direction="column" gap="8" width="100%" align_items="center" overflow_y="scroll">
<div id="list_parent" flex_grow="1" flex_wrap="wrap" flex_direction="row" gap="4" width="100%" justify_content="center"></div>
</div>
</elements>
@@ -20,7 +20,7 @@
<rectangle position="absolute" width="100%" height="3" round="4" color="highlight(opacity-0.1)" />
<!-- Top bar contents -->
<div gap="16" align_items="center">
<div gap="16" align_items="center" id="top_bar_contents">
<!-- Back button -->
<Button id="but_back" width="48" height="48" color="on_background_contrast(transparent)" border_color="on_background_contrast(transparent)">
<sprite src_builtin="@/dashboard/back.svg" width="24" height="24" color="on_background_contrast" />
@@ -1,7 +1,9 @@
<layout version="2">
<elements>
<div id="list" gap="8" flex_wrap="wrap" align_self="baseline">
<!-- filled-in at runtime -->
<div flex_direction="column" overflow_y="scroll">
<div id="list" gap="8" flex_direction="row" flex_wrap="wrap">
<!-- filled-in at runtime -->
</div>
</div>
</elements>
</layout>
@@ -1,11 +1,11 @@
<layout version="2">
<elements>
<div flex_direction="column" gap="8" width="100%" align_items="center">
<div flex_direction="column" gap="8" width="100%" >
<div flex_direction="row" gap="4" align_self="end">
<Button id="btn_refresh" tooltip="RELOAD_FROM_DISK" width="32" height="32" sprite_src_builtin="@/dashboard/refresh.svg" />
<Button id="btn_download_skymaps" height="32" translation="APP_SETTINGS.BROWSE_ONLINE_CATALOG" sprite_src_builtin="@/dashboard/download.svg"/>
</div>
<div id="list_parent" gap="8" flex_direction="row" flex_wrap="wrap" />
<div id="list_parent" gap="8" flex_direction="row" flex_wrap="wrap" overflow_y="scroll" justify_content="center"/>
</div>
</elements>
</layout>
@@ -15,9 +15,12 @@
flex_direction="column"
gap="4"
width="256"
max_width="450"
height="200"
align_items="center"
flex_grow="1"
align_self="start">
<image id="image_preview" width="100%" height="128" round="6">
<image id="image_preview" width="100%" aspect_ratio="2.0" round="6">
<!-- new_pass is required, because we need to render rectangles at the top of the image. Sorry. -->
<div new_pass="1" id="resolution_pips" gap="4" margin="6"/>
</image>
+2 -2
View File
@@ -1,7 +1,7 @@
use std::{marker::PhantomData, rc::Rc};
use wgui::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
assets::AssetPathRef,
color::{WguiColor, WguiColorName, WguiNamedColor},
components::{ComponentTrait, button::ComponentButton},
@@ -61,7 +61,7 @@ fn get_supporter_anim(
Animation::new_ex(
btn.base().get_id(),
1234,
480,
AnimationDuration::SecondsFixed(8.0),
AnimationEasing::Linear,
Box::new(move |common, data| {
let opacity_in = f32::clamp(data.pos * FADE_SPEED, 0.0, 1.0);
+52 -10
View File
@@ -3,7 +3,9 @@ use std::{
rc::{Rc, Weak},
};
use glam::Vec2;
use wgui::{
animation::{Animation, AnimationDuration, AnimationEasing},
assets::AssetPathRef,
components::button::ComponentButton,
event::{EventAlterables, StyleSetRequest},
@@ -12,7 +14,7 @@ use wgui::{
layout::{Layout, LayoutTask, LayoutTasks, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState},
taffy::Rect,
widget::label::WidgetLabel,
widget::{WidgetObj, label::WidgetLabel},
};
use wlx_common::config::GeneralConfig;
@@ -251,6 +253,13 @@ impl State {
}
}
struct MountPopupPrepareResult {
popup_handle: PopupHandle,
id_content: WidgetID,
id_top_bar_contents: WidgetID,
id_popup_title: WidgetID,
}
impl PopupManager {
pub fn new(params: PopupManagerParams) -> Self {
Self {
@@ -273,7 +282,7 @@ impl PopupManager {
frontend_tasks: &FrontendTasks,
popup_title: &Translation,
popup_padding: PopupPadding,
) -> anyhow::Result<(PopupHandle, WidgetID /* content widget ID */)> {
) -> anyhow::Result<MountPopupPrepareResult> {
let doc_params = &ParseDocumentParams {
globals: globals.clone(),
path: AssetPathRef::BuiltIn("gui/view/popup_window.xml"),
@@ -283,6 +292,7 @@ impl PopupManager {
let id_root = state.get_widget_id("root")?;
let id_content = state.get_widget_id("content")?;
let id_top_bar_contents = state.get_widget_id("top_bar_contents")?;
let padding = match popup_padding {
PopupPadding::Normal => 16.0_f32,
@@ -294,10 +304,11 @@ impl PopupManager {
StyleSetRequest::Padding(Rect::length(padding)),
));
{
let id_popup_title = {
let mut label_title = state.fetch_widget_as::<WidgetLabel>(&layout.state, "popup_title")?;
label_title.set_text_simple(&mut globals.get(), popup_title.clone());
}
label_title.get_id()
};
let but_back = state.fetch_component_as::<ComponentButton>("but_back")?;
@@ -338,7 +349,13 @@ impl PopupManager {
});
frontend_tasks.push(FrontendTask::RefreshPopupManager);
Ok((popup_handle, id_content))
Ok(MountPopupPrepareResult {
popup_handle,
id_content,
id_top_bar_contents,
id_popup_title,
})
}
/// Mount a new popup on top of the existing popup stack.
@@ -356,18 +373,43 @@ impl PopupManager {
anyhow::bail!("mount_popup_once called more than once");
};
let (popup_handle, id_content) =
self.mount_popup_prepare(globals, layout, frontend_tasks, &params.title, params.extra.padding)?;
let res = self.mount_popup_prepare(globals, layout, frontend_tasks, &params.title, params.extra.padding)?;
// text fade-in
Animation::effect_label_fade_in(
res.id_popup_title,
AnimationDuration::Seconds(0.25),
AnimationEasing::OutQuad,
)
.submit_l(layout);
// top bar slide
Animation::effect_slide(
res.id_top_bar_contents,
AnimationDuration::Seconds(0.5),
AnimationEasing::OutQuint,
Vec2::new(10.0, 0.0),
)
.submit_l(layout);
// content slide
Animation::effect_slide(
res.id_content,
AnimationDuration::Seconds(0.5),
AnimationEasing::OutQuint,
Vec2::new(5.0, 0.0),
)
.submit_l(layout);
// mount user-set popup content
let closed_callback = on_content_func(PopupContentFuncData {
layout,
handle: popup_handle.clone(),
id_content,
handle: res.popup_handle.clone(),
id_content: res.id_content,
config,
})?;
popup_handle.state.borrow_mut().closed_callback = Some(closed_callback);
res.popup_handle.state.borrow_mut().closed_callback = Some(closed_callback);
Ok(())
}
+9 -6
View File
@@ -1,7 +1,7 @@
use glam::{Mat4, Vec3};
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
use wgui::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
color::{WguiColor, WguiColorName},
components::tooltip::{TOOLTIP_BORDER_COLOR, TOOLTIP_COLOR},
i18n::Translation,
@@ -45,7 +45,9 @@ impl Drop for MountedToast {
}
}
const TOAST_DURATION_TICKS: u32 = 150;
fn def_toast_duration() -> AnimationDuration {
AnimationDuration::Seconds(2.5)
}
impl ToastManager {
pub fn new() -> Self {
@@ -116,9 +118,9 @@ impl ToastManager {
let (label, _) = layout.add_child(rect.id, label, taffy::Style { ..Default::default() })?;
// show-up animation
layout.animations.add(Animation::new(
Animation::new(
rect.id,
(TOAST_DURATION_TICKS as f32 * layout.state.theme.animation_mult) as u32,
def_toast_duration(),
AnimationEasing::Linear,
Box::new(move |common, data| {
let pos_showup = AnimationEasing::OutQuint.interpolate((data.pos * 4.0).min(1.0));
@@ -143,7 +145,8 @@ impl ToastManager {
);
common.alterables.mark_redraw();
}),
));
)
.submit_l(layout);
state.toast = Some(MountedToast {
id_root: root.id,
@@ -165,7 +168,7 @@ impl ToastManager {
if state.timeout == 0 {
state.toast = None;
state.timeout = TOAST_DURATION_TICKS;
state.timeout = def_toast_duration().to_ticks(layout.state.ticks_per_second, layout.state.theme.animation_mult);
// mount next
if let Some(content) = state.queue.pop_front() {
self.mount_toast(layout, &mut state, content)?;
+5 -4
View File
@@ -1,6 +1,6 @@
use glam::{Mat4, Vec2};
use wgui::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
assets::AssetPathRef,
color::WguiColorName,
components::{self, button::ButtonClickCallback},
@@ -145,9 +145,9 @@ pub fn create_loading(par: CreateLoadingParams) -> anyhow::Result<WidgetID> {
let id_root = data.get_widget_id("root")?;
let id_sprite_loading = data.get_widget_id("sprite_loading")?;
par.layout.animations.add(Animation::new(
Animation::new(
id_sprite_loading,
60 * 30, /* spin it for 30 seconds at most */
AnimationDuration::SecondsFixed(120.0), /* spin it for 120 seconds at most */
AnimationEasing::Linear,
Box::new(move |common, data| {
// spin it
@@ -161,7 +161,8 @@ pub fn create_loading(par: CreateLoadingParams) -> anyhow::Result<WidgetID> {
}
common.alterables.mark_redraw();
}),
));
)
.submit_l(par.layout);
Ok(id_root)
}
+8 -1
View File
@@ -14,7 +14,7 @@ use wgui::{
layout::{Layout, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState, TemplateParams},
task::Tasks,
widget::ConstructEssentials,
widget::{ConstructEssentials, rectangle::WidgetRectangle},
};
use crate::{
@@ -802,6 +802,13 @@ impl View {
par,
)?;
if params.checked {
let rect = data.fetch_widget(&params.layout.state, "rect")?;
let mut rect = rect.widget.cast::<WidgetRectangle>()?;
rect.set_color(&mut params.layout.common(), WguiColorName::BackgroundVariant.into());
rect.set_border_color(&mut params.layout.common(), WguiColorName::Primary.into());
}
let mut common = params.layout.common();
let checkbox = data.fetch_component_as::<ComponentCheckbox>("checkbox")?;
let btn_mute = data.fetch_component_as::<ComponentButton>("btn_mute")?;
+8 -7
View File
@@ -6,22 +6,23 @@
padding="16"
flex_direction="column"
gap="8"
color="background" border_color="outline" border="2" round="8">
color="background" border_color="outline" border="2" round="8"
id="rect">
<!-- Toast title -->
<div flex_direction="row" align_items="center" gap="8">
<sprite src="@/icons/bell.svg" color="primary" min_width="32" min_height="32" max_width="32" max_height="32" flex_grow="1" />
<label wrap="1" id="toast_title" color="primary" weight="bold" size="25" padding_left="16" padding_right="16" />
<div flex_direction="row" align_items="center" gap="8" id="div_title">
<sprite id="sprite_bell" src="@/icons/bell.svg" color="primary" min_width="32" min_height="32" max_width="32" max_height="32" flex_grow="1" />
<label wrap="1" id="label_title" color="primary" weight="bold" size="25" padding_left="16" padding_right="16" />
</div>
<!-- A simple separator -->
<rectangle width="100%" height="2" color="outline" />
<rectangle id="rect_separator" width="100%" height="2" color="outline" />
<div>
<!-- Toast message -->
<label id="toast_body" color="on_background" wrap="1" size="20" padding_left="16" padding_right="16" />
<label id="label_body" color="on_background" wrap="1" size="20" padding_left="16" padding_right="16" />
</div>
</rectangle>
</div>
</elements>
</layout>
</layout>
+1
View File
@@ -625,6 +625,7 @@ pub(super) fn setup_custom_button<S: 'static>(
)
.with_timeout(1.0)
.with_lerp_amount(1.0)
.with_animate(false)
.with_sound(i == 0)
.submit_at(app, now + Duration::from_secs(i as _));
}
+4 -8
View File
@@ -2,7 +2,7 @@ use std::rc::Rc;
use anyhow::Context;
use wgui::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
color::{WguiColor, WguiColorName},
components::button::ComponentButton,
event::CallbackDataCommon,
@@ -76,11 +76,7 @@ impl InteractLockHandler {
}
}
pub fn toggle(
&mut self,
common: &mut CallbackDataCommon,
anim_mult: f32,
) -> Box<ModifyOverlayTask> {
pub fn toggle(&mut self, common: &mut CallbackDataCommon) -> Box<ModifyOverlayTask> {
let rect_color = self.color;
self.interactable = !self.interactable;
@@ -91,7 +87,7 @@ impl InteractLockHandler {
let anim = if self.interactable {
Animation::new(
self.id,
(10. * anim_mult) as _,
AnimationDuration::Seconds(0.1666),
AnimationEasing::OutQuad,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
@@ -108,7 +104,7 @@ impl InteractLockHandler {
} else {
Animation::new(
self.id,
(10. * anim_mult) as _,
AnimationDuration::Seconds(0.1666),
AnimationEasing::OutBack,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
+1 -3
View File
@@ -365,8 +365,6 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
resize: ResizeState::None,
};
let anim_mult = app.wgui_theme.animation_mult;
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| {
let Ok(button) = parser
.fetch_component_from_widget_id_as::<ComponentButton>(&layout.state, attribs.widget_id)
@@ -395,7 +393,7 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
}
let sel = OverlaySelector::Id(*state.id.borrow());
let task = state.lock.toggle(common, anim_mult);
let task = state.lock.toggle(common);
app.tasks
.enqueue(TaskType::Overlay(OverlayTask::Modify(sel, task)));
Ok(EventResult::Consumed)
+11 -13
View File
@@ -15,7 +15,7 @@ use anyhow::Context;
use glam::{FloatExt, Mat4, Vec2, vec2, vec3};
use smallvec::{SmallVec, smallvec};
use wgui::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
assets::AssetPathRef,
color::{WguiColor, WguiColorName},
event::{self, CallbackMetadata, EventListenerKind},
@@ -74,8 +74,6 @@ pub(super) fn create_keyboard_panel(
let doc_params = new_doc_params(&mut panel);
let anim_mult = app.wgui_theme.animation_mult;
let root = panel
.parser_state
.get_widget_id("keyboard_root")
@@ -206,7 +204,7 @@ pub(super) fn create_keyboard_panel(
let k = key_state.clone();
move |common, data, _app, _state| {
common.alterables.trigger_haptics();
on_enter_anim(k.clone(), common, data, anim_mult, width_mul);
on_enter_anim(k.clone(), common, data, width_mul);
Ok(EventResult::Pass)
}
}),
@@ -218,7 +216,7 @@ pub(super) fn create_keyboard_panel(
let k = key_state.clone();
move |common, data, _app, _state| {
common.alterables.trigger_haptics();
on_leave_anim(k.clone(), common, data, anim_mult, width_mul);
on_leave_anim(k.clone(), common, data, width_mul);
Ok(EventResult::Pass)
}
}),
@@ -415,12 +413,11 @@ fn on_enter_anim(
key_state: Rc<KeyState>,
common: &mut event::CallbackDataCommon,
data: &event::CallbackData,
anim_mult: f32,
width_mult: f32,
) {
common.alterables.animate(Animation::new(
Animation::new(
data.widget_id,
(10. * anim_mult) as _,
AnimationDuration::Seconds(0.1666),
AnimationEasing::OutBack,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
@@ -456,19 +453,19 @@ fn on_enter_anim(
get_anim_transform(data.pos, data.widget_boundary.size, width_mult);
common.alterables.mark_redraw();
}),
));
)
.submit(common.alterables);
}
fn on_leave_anim(
key_state: Rc<KeyState>,
common: &mut event::CallbackDataCommon,
data: &event::CallbackData,
anim_mult: f32,
width_mult: f32,
) {
common.alterables.animate(Animation::new(
Animation::new(
data.widget_id,
(15. * anim_mult) as _,
AnimationDuration::Seconds(0.25),
AnimationEasing::OutQuad,
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
@@ -506,7 +503,8 @@ fn on_leave_anim(
get_anim_transform(1.0 - data.pos, data.widget_boundary.size, width_mult);
common.alterables.mark_redraw();
}),
));
)
.submit(common.alterables);
}
fn on_press_anim(
+115 -5
View File
@@ -5,9 +5,11 @@ use std::{
};
use anyhow::Context;
use glam::{Affine3A, Quat, Vec3, vec3};
use glam::{Affine3A, Quat, Vec2, Vec3, vec3};
use wgui::{
animation::{Animation, AnimationDuration, AnimationEasing, AnimationStartDelay},
i18n::{I18n, Translation},
parser::Fetchable,
widget::label::WidgetLabel,
};
use wlx_common::{
@@ -30,6 +32,7 @@ pub struct ToastParams {
pub opacity: f32,
pub timeout: f32,
pub lerp_amount: f32,
pub animate: bool,
pub sound: bool,
pub topic: ToastTopic,
}
@@ -56,6 +59,7 @@ impl Toast {
opacity: 1.0,
lerp_amount: 0.1,
timeout: 3.0,
animate: true,
sound: false,
topic,
},
@@ -82,6 +86,11 @@ impl Toast {
self
}
pub const fn with_animate(mut self, animate: bool) -> Self {
self.params.animate = animate;
self
}
pub fn submit(self, app: &mut AppState) {
self.submit_at(app, Instant::now());
}
@@ -200,21 +209,21 @@ fn new_toast(toast: BakedToast, app: &mut AppState) -> Option<OverlayWindowConfi
let on_custom_id: OnCustomIdFunc<()> =
Box::new(move |id, widget, _doc_params, layout, _parser_state, ()| {
if &*id == "toast_title" {
if &*id == "label_title" {
let mut label = layout
.state
.widgets
.get_as::<WidgetLabel>(widget)
.context("toast.xml: missing element with id: toast_title")?;
.context("toast.xml: missing element with id: label_title")?;
let mut globals = layout.state.globals.get();
label.set_text_simple(&mut globals, title.clone());
}
if &*id == "toast_body" {
if &*id == "label_body" {
let mut label = layout
.state
.widgets
.get_as::<WidgetLabel>(widget)
.context("toast.xml: missing element with id: toast_body")?;
.context("toast.xml: missing element with id: label_body")?;
let mut globals = layout.state.globals.get();
label.set_text_simple(&mut globals, body.clone());
}
@@ -233,6 +242,107 @@ fn new_toast(toast: BakedToast, app: &mut AppState) -> Option<OverlayWindowConfi
.inspect_err(|e| log::error!("Could not create toast: {e:?}"))
.ok()?;
// animations
if toast.params.animate {
let id_div_title = panel.parser_state.get_widget_id("div_title").ok()?;
let id_label_body = panel.parser_state.get_widget_id("label_body").ok()?;
let id_label_title = panel.parser_state.get_widget_id("label_title").ok()?;
let id_rect = panel.parser_state.get_widget_id("rect").ok()?;
let id_rect_separator = panel.parser_state.get_widget_id("rect_separator").ok()?;
let id_sprite_bell = panel.parser_state.get_widget_id("sprite_bell").ok()?;
// schedule timeouts
let time_window = 0.5;
if toast.params.timeout > time_window && toast.params.timeout < 150.0 {
let dur = AnimationDuration::SecondsFixed(time_window);
let easing = AnimationEasing::OutQuad;
let mut add = |a: Animation| {
a.reversed()
.delayed(AnimationStartDelay::SecondsFixed(
toast.params.timeout - time_window,
))
.with_id(1)
.submit_l(&mut panel.layout);
};
add(Animation::effect_rectangle_fade_in(id_rect, dur, easing));
add(Animation::effect_scale(
id_rect_separator,
dur,
easing,
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 1.0),
));
add(Animation::effect_label_fade_in(id_label_title, dur, easing));
add(Animation::effect_label_fade_in(id_label_body, dur, easing));
add(Animation::effect_sprite_fade_in(
id_sprite_bell,
dur,
easing,
));
}
// separator scale
Animation::effect_scale(
id_rect_separator,
AnimationDuration::Seconds(1.0),
AnimationEasing::OutBack,
Vec2::new(0.0, 1.0),
Vec2::new(1.0, 1.0),
)
.submit_l(&mut panel.layout);
// rectangle opacity
Animation::effect_rectangle_fade_in(
id_rect,
AnimationDuration::Seconds(0.5),
AnimationEasing::OutQuad,
)
.submit_l(&mut panel.layout);
// title animation
Animation::effect_slide(
id_div_title,
AnimationDuration::Seconds(1.0),
AnimationEasing::OutQuint,
Vec2::new(15.0, 0.0),
)
.submit_l(&mut panel.layout);
Animation::effect_label_fade_in(
id_label_title,
AnimationDuration::Seconds(0.5),
AnimationEasing::OutQuad,
)
.submit_l(&mut panel.layout);
Animation::effect_sprite_fade_in(
id_sprite_bell,
AnimationDuration::Seconds(0.5),
AnimationEasing::OutQuad,
)
.submit_l(&mut panel.layout);
// body animation
Animation::effect_slide(
id_label_body,
AnimationDuration::Seconds(1.0),
AnimationEasing::OutQuint,
Vec2::new(15.0, 0.0),
)
.delayed(AnimationStartDelay::Seconds(0.3))
.submit_l(&mut panel.layout);
Animation::effect_label_fade_in(
id_label_body,
AnimationDuration::Seconds(0.5),
AnimationEasing::OutQuint,
)
.delayed(AnimationStartDelay::Seconds(0.3))
.submit_l(&mut panel.layout);
}
panel
.update_layout(app)
.context("layout update failed")
+5 -4
View File
@@ -2,7 +2,7 @@ use std::{cell::RefCell, rc::Rc, time::Duration};
use glam::Affine3A;
use wgui::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
color::WguiColor,
components::button::ComponentButton,
drawing::Color,
@@ -97,10 +97,10 @@ fn start_progress_state(
let id_rect_vu_meter = state.id_rect_vu_meter;
let id_label_progress = state.id_label_progress;
common.alterables.animate(Animation::new_ex(
Animation::new_ex(
id_rect_vu_meter, /* any tbh */
0,
60 * 120, // max 120 seconds (whisper_stt MAX_DURATION is currently set to 30, account for processing time)
AnimationDuration::SecondsFixed(120.0), // max 120 seconds (whisper_stt MAX_DURATION is currently set to 30, account for processing time)
AnimationEasing::Linear,
Box::new(move |common, data| {
let rect_vu_meter = data.obj.cast_mut::<WidgetRectangle>().unwrap();
@@ -156,7 +156,8 @@ fn start_progress_state(
);
}
}),
));
)
.submit(common.alterables);
}
fn reset_progress_state(common: &mut CallbackDataCommon, state: &WhisperState) {
+217 -17
View File
@@ -1,12 +1,14 @@
use glam::FloatExt;
use glam::{FloatExt, Mat4, Vec2, Vec3};
use crate::{
drawing::Boundary,
event::{CallbackDataCommon, EventAlterables},
layout::{LayoutState, WidgetID},
widget::{WidgetData, WidgetObj},
layout::{Layout, LayoutState, WidgetID},
renderer_vk::{self},
widget::{WidgetData, WidgetObj, label::WidgetLabel, rectangle::WidgetRectangle, sprite::WidgetSprite},
};
#[derive(Clone, Copy)]
pub enum AnimationEasing {
Linear,
InQuad, // ^2
@@ -53,12 +55,55 @@ pub struct CallbackData<'a> {
}
pub type AnimationCallback = Box<dyn Fn(&mut CallbackDataCommon, &mut CallbackData)>;
#[derive(Clone, Copy)]
pub enum AnimationDuration {
Seconds(f32), // multiplied by animation_mult
SecondsFixed(f32),
Infinity,
}
#[derive(Clone, Copy)]
pub enum AnimationStartDelay {
Seconds(f32), // multiplied by animation_mult
SecondsFixed(f32),
}
impl AnimationDuration {
pub fn to_ticks(&self, ticks_per_seconds: u32, animation_mult: f32) -> u32 {
match self {
AnimationDuration::Seconds(secs) => (secs * ticks_per_seconds as f32 * animation_mult) as u32,
AnimationDuration::SecondsFixed(secs) => (secs * ticks_per_seconds as f32) as u32,
AnimationDuration::Infinity => u32::MAX,
}
}
}
impl AnimationStartDelay {
pub fn to_ticks(&self, ticks_per_seconds: u32, animation_mult: f32) -> u32 {
match self {
AnimationStartDelay::Seconds(secs) => (secs * ticks_per_seconds as f32 * animation_mult) as u32,
AnimationStartDelay::SecondsFixed(secs) => (secs * ticks_per_seconds as f32) as u32,
}
}
}
struct Ticks {
delay: u32,
remaining: u32,
duration: u32,
}
pub struct Animation {
target_widget: WidgetID,
id: u32,
ticks_remaining: u32,
ticks_duration: u32,
duration: AnimationDuration,
start_delay_secs: Option<AnimationStartDelay>,
reversed: bool,
// filled-in at first iteration
ticks: Option<Ticks>,
easing: AnimationEasing,
@@ -70,14 +115,136 @@ pub struct Animation {
}
impl Animation {
pub fn new(target_widget: WidgetID, ticks: u32, easing: AnimationEasing, callback: AnimationCallback) -> Self {
Self::new_ex(target_widget, 0, ticks, easing, callback)
pub fn new(
target_widget: WidgetID,
duration: AnimationDuration,
easing: AnimationEasing,
callback: AnimationCallback,
) -> Self {
Self::new_ex(target_widget, 0, duration, easing, callback)
}
#[must_use]
pub const fn delayed(self, delay: AnimationStartDelay) -> Self {
let mut this = self;
this.start_delay_secs = Some(delay);
this
}
#[must_use]
pub const fn with_id(self, id: u32) -> Self {
let mut this = self;
this.id = id;
this
}
// invert pos with 1.0 - pos
#[must_use]
pub const fn reversed(self) -> Self {
let mut this = self;
this.reversed = true;
this
}
pub fn submit(self, alterables: &mut EventAlterables) {
alterables.animate(self);
}
pub fn submit_l(self, layout: &mut Layout) {
self.submit(&mut layout.alterables);
}
pub fn effect_slide(
target_widget: WidgetID,
duration: AnimationDuration,
easing: AnimationEasing,
dir: Vec2,
) -> Self {
Animation::new(
target_widget,
duration,
easing,
Box::new(move |common, data| {
data.data.transform =
Mat4::from_translation(Vec3::new((1.0 - data.pos) * dir.x, (1.0 - data.pos) * dir.y, 0.0));
common.alterables.mark_redraw();
}),
)
}
pub fn effect_scale(
target_widget: WidgetID,
duration: AnimationDuration,
easing: AnimationEasing,
from: Vec2,
to: Vec2, // (1.0, 1.0) - normal scale
) -> Self {
Animation::new(
target_widget,
duration,
easing,
Box::new(move |common, data| {
let scale = Vec3::new(from.x.lerp(to.x, data.pos), from.y.lerp(to.y, data.pos), 0.0);
data.data.transform = renderer_vk::util::centered_matrix(data.widget_boundary.size, &Mat4::from_scale(scale));
common.alterables.mark_redraw();
}),
)
}
pub fn effect_label_fade_in(target_widget: WidgetID, duration: AnimationDuration, easing: AnimationEasing) -> Self {
Animation::new(
target_widget,
duration,
easing,
Box::new(move |common, data| {
let Ok(label) = data.obj.cast_mut::<WidgetLabel>() else {
debug_assert!(false); // cast failed
return;
};
label.set_color(common, label.get_color().with_alpha(data.pos), true);
}),
)
}
pub fn effect_rectangle_fade_in(
target_widget: WidgetID,
duration: AnimationDuration,
easing: AnimationEasing,
) -> Self {
Animation::new(
target_widget,
duration,
easing,
Box::new(move |common, data| {
let Ok(rectangle) = data.obj.cast_mut::<WidgetRectangle>() else {
debug_assert!(false); // cast failed
return;
};
rectangle.set_color(common, rectangle.get_color().with_alpha(data.pos));
rectangle.set_border_color(common, rectangle.get_border_color().with_alpha(data.pos));
}),
)
}
pub fn effect_sprite_fade_in(target_widget: WidgetID, duration: AnimationDuration, easing: AnimationEasing) -> Self {
Animation::new(
target_widget,
duration,
easing,
Box::new(move |common, data| {
let Ok(sprite) = data.obj.cast_mut::<WidgetSprite>() else {
debug_assert!(false); // cast failed
return;
};
sprite.set_color(common, sprite.get_color().with_alpha(data.pos));
}),
)
}
pub fn new_ex(
target_widget: WidgetID,
animation_id: u32,
ticks: u32,
duration: AnimationDuration,
easing: AnimationEasing,
callback: AnimationCallback,
) -> Self {
@@ -86,21 +253,25 @@ impl Animation {
id: animation_id,
callback,
easing,
ticks_duration: ticks,
ticks_remaining: ticks,
duration,
ticks: None,
last_tick: false,
pos: 0.0,
pos_prev: 0.0,
start_delay_secs: None,
reversed: false,
}
}
/// @returns false if it wants to be stopped
#[must_use]
fn call(&self, state: &LayoutState, alterables: &mut EventAlterables, pos: f32) -> bool {
fn call(&self, state: &LayoutState, alterables: &mut EventAlterables, raw_pos: f32) -> bool {
let Some(widget) = state.widgets.get(self.target_widget).cloned() else {
return false; // failed
};
let pos = if self.reversed { 1.0 - raw_pos } else { raw_pos };
let mut widget_state = widget.state();
let (data, obj) = widget_state.get_data_obj_mut();
let mut stop_me = false;
@@ -130,8 +301,27 @@ pub struct Animations {
impl Animations {
pub fn tick(&mut self, state: &LayoutState, alterables: &mut EventAlterables) {
for anim in &mut self.running_animations {
let x = 1.0 - (anim.ticks_remaining as f32 / anim.ticks_duration as f32);
let pos = if anim.ticks_remaining > 0 {
let mut ticks = anim.ticks.take().unwrap_or_else(|| {
let ticks = anim
.duration
.to_ticks(state.ticks_per_second, state.theme.animation_mult);
Ticks {
remaining: ticks,
duration: ticks,
delay: anim.start_delay_secs.map_or(0, |delay| {
delay.to_ticks(state.ticks_per_second, state.theme.animation_mult)
}),
}
});
if ticks.delay > 0 {
ticks.delay -= 1;
anim.ticks = Some(ticks);
continue;
}
let x = 1.0 - (ticks.remaining as f32 / ticks.duration as f32);
let pos = if ticks.remaining > 0 {
anim.easing.interpolate(x)
} else {
anim.last_tick = true;
@@ -145,8 +335,10 @@ impl Animations {
let _ = anim.call(state, alterables, 1.0);
alterables.needs_redraw = true;
} else {
anim.ticks_remaining -= 1;
ticks.remaining -= 1;
}
anim.ticks = Some(ticks);
}
self.running_animations.retain(|anim| !anim.last_tick);
@@ -155,15 +347,23 @@ impl Animations {
pub fn process(&mut self, state: &LayoutState, alterables: &mut EventAlterables, alpha: f32) {
for anim in &mut self.running_animations {
let pos = anim.pos_prev.lerp(anim.pos, alpha);
if !anim.call(state, alterables, pos) {
anim.ticks_remaining = 0;
if !anim.call(state, alterables, pos)
&& let Some(ticks) = &mut anim.ticks
{
ticks.remaining = 0;
}
}
}
pub fn add(&mut self, anim: Animation) {
pub fn add(&mut self, anim: Animation, state: &LayoutState, alterables: &mut EventAlterables) {
// prevent running two animations at once
self.stop_by_widget(anim.target_widget, Some(anim.id));
// call the animation for the first time with pos 0.0
if !anim.call(state, alterables, 0.0) {
return;
}
self.running_animations.push(anim);
}
+11 -19
View File
@@ -1,5 +1,5 @@
use crate::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
assets::AssetPathRef,
color::{ParentColor, WguiColor, WguiColorName},
components::{
@@ -266,17 +266,15 @@ impl ComponentButton {
return;
}
let (anim_mult, gradient_intensity) = {
let gradient_intensity = {
let theme = &common.state.theme;
(theme.animation_mult, theme.gradient_intensity)
theme.gradient_intensity
};
let anim_ticks = if sticky_down { 5. } else { 10. };
let state = self.state.clone();
let anim = Animation::new(
self.data.id_rect,
(anim_ticks * anim_mult) as _,
AnimationDuration::Seconds(if sticky_down { 0.083 } else { 0.1666 }),
AnimationEasing::OutCubic,
Box::new(move |common, anim_data| {
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
@@ -383,10 +381,10 @@ fn anim_hover(
rect.params.border_color = init_border_color.lerp(&globals.palette, &colors.hover_border_color, mult);
}
fn anim_hover_create(state: Rc<RefCell<State>>, widget_id: WidgetID, fade_in: bool, anim_mult: f32) -> Animation {
fn anim_hover_create(state: Rc<RefCell<State>>, widget_id: WidgetID, fade_in: bool) -> Animation {
Animation::new(
widget_id,
((if fade_in { 5. } else { 10. }) * anim_mult) as _,
AnimationDuration::Seconds(if fade_in { 0.0833 } else { 0.1666 }),
AnimationEasing::OutCubic,
Box::new(move |common, anim_data| {
let rect = anim_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
@@ -413,7 +411,6 @@ fn register_event_mouse_enter(
state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection,
tooltip_info: Option<components::tooltip::TooltipInfo>,
anim_mult: f32,
) -> EventListenerID {
listeners.register(
EventListenerKind::MouseEnter,
@@ -423,7 +420,7 @@ fn register_event_mouse_enter(
common.alterables.mark_redraw();
common
.alterables
.animate(anim_hover_create(state.clone(), event_data.widget_id, true, anim_mult));
.animate(anim_hover_create(state.clone(), event_data.widget_id, true));
ComponentTooltip::register_hover_in(common, &tooltip_info, data.id_rect, state.clone());
@@ -433,18 +430,14 @@ fn register_event_mouse_enter(
)
}
fn register_event_mouse_leave(
state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection,
anim_mult: f32,
) -> EventListenerID {
fn register_event_mouse_leave(state: Rc<RefCell<State>>, listeners: &mut EventListenerCollection) -> EventListenerID {
listeners.register(
EventListenerKind::MouseLeave,
Box::new(move |common, event_data, (), ()| {
common.alterables.trigger_haptics();
common
.alterables
.animate(anim_hover_create(state.clone(), event_data.widget_id, false, anim_mult));
.animate(anim_hover_create(state.clone(), event_data.widget_id, false));
let mut state = state.borrow_mut();
state.active_tooltip = None;
state.hovered = false;
@@ -702,11 +695,10 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
id: root.id,
lhandles: {
let listeners = &mut root.widget.state().event_listeners;
let anim_mult = ess.layout.state.theme.animation_mult;
vec![
register_event_mouse_cancel(state.clone(), listeners),
register_event_mouse_enter(data.clone(), state.clone(), listeners, params.tooltip, anim_mult),
register_event_mouse_leave(state.clone(), listeners, anim_mult),
register_event_mouse_enter(data.clone(), state.clone(), listeners, params.tooltip),
register_event_mouse_leave(state.clone(), listeners),
register_event_mouse_press(state.clone(), listeners),
register_event_mouse_release(data.clone(), state.clone(), listeners),
]
+9 -12
View File
@@ -8,7 +8,7 @@ use taffy::{
};
use crate::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
color::{WguiColor, WguiColorName},
components::{
Component, ComponentBase, ComponentTrait, DestroyData, RefreshData,
@@ -182,11 +182,11 @@ fn anim_hover(anim_data: &mut crate::animation::CallbackData<'_>, pos: f32, _pre
};
}
fn anim_hover_in(state: &Rc<RefCell<State>>, data: &Rc<Data>, anim_mult: f32) -> Animation {
fn anim_hover_in(state: &Rc<RefCell<State>>, data: &Rc<Data>) -> Animation {
let down = state.borrow().down;
Animation::new(
data.id_outer_box,
(5. * anim_mult) as _,
AnimationDuration::Seconds(0.0833),
AnimationEasing::OutQuad,
Box::new(move |common, anim_data| {
anim_hover(anim_data, anim_data.pos, down);
@@ -195,11 +195,11 @@ fn anim_hover_in(state: &Rc<RefCell<State>>, data: &Rc<Data>, anim_mult: f32) ->
)
}
fn anim_hover_out(state: &Rc<RefCell<State>>, data: &Rc<Data>, anim_mult: f32) -> Animation {
fn anim_hover_out(state: &Rc<RefCell<State>>, data: &Rc<Data>) -> Animation {
let down = state.borrow().down;
Animation::new(
data.id_outer_box,
(8. * anim_mult) as _,
AnimationDuration::Seconds(0.0833),
AnimationEasing::OutQuad,
Box::new(move |common, anim_data| {
anim_hover(anim_data, 1.0 - anim_data.pos, down);
@@ -213,13 +213,12 @@ fn register_event_mouse_enter(
data: Rc<Data>,
listeners: &mut EventListenerCollection,
tooltip_info: Option<tooltip::TooltipInfo>,
anim_mult: f32,
) -> EventListenerID {
listeners.register(
EventListenerKind::MouseEnter,
Box::new(move |common, _event_data, (), ()| {
common.alterables.trigger_haptics();
common.alterables.animate(anim_hover_in(&state, &data, anim_mult));
anim_hover_in(&state, &data).submit(common.alterables);
ComponentTooltip::register_hover_in(common, &tooltip_info, data.id_container, state.clone());
@@ -247,13 +246,12 @@ fn register_event_mouse_leave(
state: Rc<RefCell<State>>,
data: Rc<Data>,
listeners: &mut EventListenerCollection,
anim_mult: f32,
) -> EventListenerID {
listeners.register(
EventListenerKind::MouseLeave,
Box::new(move |common, _event_data, (), ()| {
common.alterables.trigger_haptics();
common.alterables.animate(anim_hover_out(&state, &data, anim_mult));
anim_hover_out(&state, &data).submit(common.alterables);
let checked = {
let mut state = state.borrow_mut();
@@ -509,10 +507,9 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
id: root.id,
lhandles: {
let listeners = &mut root.widget.state().event_listeners;
let anim_mult = ess.layout.state.theme.animation_mult;
vec![
register_event_mouse_enter(state.clone(), data.clone(), listeners, params.tooltip, anim_mult),
register_event_mouse_leave(state.clone(), data.clone(), listeners, anim_mult),
register_event_mouse_enter(state.clone(), data.clone(), listeners, params.tooltip),
register_event_mouse_leave(state.clone(), data.clone(), listeners),
register_event_mouse_cancel(state.clone(), listeners),
register_event_mouse_press(state.clone(), data.clone(), listeners),
register_event_mouse_release(data.clone(), state.clone(), listeners),
+33 -45
View File
@@ -7,7 +7,7 @@ use glam::FloatExt;
use taffy::prelude::{auto, length, percent};
use crate::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
color::{WguiColor, WguiColorName},
components::{Component, ComponentBase, ComponentTrait, FocusChangeData, RefreshData},
event::{self, CallbackDataCommon, CallbackMetadata, EventListenerCollection, EventListenerKind, StyleSetRequest},
@@ -53,58 +53,46 @@ pub struct ComponentEditBox {
state: Rc<RefCell<State>>,
}
fn anim_bottom_rect(
common: &mut CallbackDataCommon,
accent_color: WguiColor,
id_rect: WidgetID,
anim_mult: f32,
focused: bool,
) {
common.alterables.animate(Animation::new(
id_rect,
(10.0 * anim_mult) as _,
AnimationEasing::OutQuad,
{
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
let pos_bidir = if focused { data.pos } else { 1.0 - data.pos };
fn anim_bottom_rect(common: &mut CallbackDataCommon, accent_color: WguiColor, id_rect: WidgetID, focused: bool) {
Animation::new(id_rect, AnimationDuration::Seconds(0.1666), AnimationEasing::OutQuad, {
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
let pos_bidir = if focused { data.pos } else { 1.0 - data.pos };
let color_lerped = accent_color.lerp(
&common.globals().palette,
&WguiColorName::OnBackgroundVariant.into(),
pos_bidir,
);
rect.set_color(common, color_lerped);
let color_lerped = accent_color.lerp(
&common.globals().palette,
&WguiColorName::OnBackgroundVariant.into(),
pos_bidir,
);
rect.set_color(common, color_lerped);
common.alterables.set_style(
data.widget_id,
StyleSetRequest::Size(taffy::Size {
width: percent(0.95.lerp(1.0, pos_bidir)),
height: length(1.0 + pos_bidir),
}),
);
common.alterables.set_style(
data.widget_id,
StyleSetRequest::Size(taffy::Size {
width: percent(0.95.lerp(1.0, pos_bidir)),
height: length(1.0 + pos_bidir),
}),
);
common.alterables.set_style(
data.widget_id,
StyleSetRequest::Margin(taffy::Rect {
bottom: length(pos_bidir),
left: auto(),
right: auto(),
top: auto(),
}),
);
common.alterables.set_style(
data.widget_id,
StyleSetRequest::Margin(taffy::Rect {
bottom: length(pos_bidir),
left: auto(),
right: auto(),
top: auto(),
}),
);
common.alterables.mark_redraw();
})
},
));
common.alterables.mark_redraw();
})
})
.submit(common.alterables);
}
fn refresh_all(common: &mut CallbackDataCommon, data: &Data, state: &mut State) -> Option<()> {
let theme = &common.state.theme;
let editbox_color = WguiColor::from(WguiColorName::BackgroundVariant);
let accent_color = WguiColor::from(WguiColorName::Primary);
let anim_mult = theme.animation_mult;
let (rect_color, border_color) = if state.focused {
(editbox_color.add_rgb(0.15), editbox_color.add_rgb(0.15 + 0.25))
@@ -120,7 +108,7 @@ fn refresh_all(common: &mut CallbackDataCommon, data: &Data, state: &mut State)
rect.set_color(common, rect_color);
if state.focused_prev != state.focused || state.first_refresh {
anim_bottom_rect(common, accent_color, data.id_rect_bottom, anim_mult, state.focused);
anim_bottom_rect(common, accent_color, data.id_rect_bottom, state.focused);
state.focused_prev = state.focused;
}
+16 -54
View File
@@ -4,7 +4,7 @@ use glam::{Mat4, Vec2, Vec3};
use taffy::prelude::{length, percent};
use crate::{
animation::{Animation, AnimationCallback, AnimationEasing},
animation::{Animation, AnimationCallback, AnimationDuration, AnimationEasing},
color::{WguiColor, WguiColorName},
components::{
Component, ComponentBase, ComponentTrait, DestroyData, RefreshData,
@@ -404,53 +404,23 @@ fn get_callback_label(inverse: bool) -> AnimationCallback {
})
}
fn on_enter_anim(
common: &mut event::CallbackDataCommon,
id_handle: WidgetID,
id_label: Option<WidgetID>,
anim_mult: f32,
) {
let duration = 20. * anim_mult;
fn on_enter_anim(common: &mut event::CallbackDataCommon, id_handle: WidgetID, id_label: Option<WidgetID>) {
let duration = AnimationDuration::Seconds(0.333);
common.alterables.animate(Animation::new(
id_handle,
duration as _,
AnimationEasing::OutBack,
get_callback_rect(false),
));
Animation::new(id_handle, duration, AnimationEasing::OutBack, get_callback_rect(false)).submit(common.alterables);
if let Some(id_label) = id_label {
common.alterables.animate(Animation::new(
id_label,
duration as _,
AnimationEasing::OutBack,
get_callback_label(false),
));
Animation::new(id_label, duration, AnimationEasing::OutBack, get_callback_label(false)).submit(common.alterables);
}
}
fn on_leave_anim(
common: &mut event::CallbackDataCommon,
id_handle: WidgetID,
id_label: Option<WidgetID>,
anim_mult: f32,
) {
let duration = 10. * anim_mult;
fn on_leave_anim(common: &mut event::CallbackDataCommon, id_handle: WidgetID, id_label: Option<WidgetID>) {
let duration = AnimationDuration::Seconds(0.0833);
common.alterables.animate(Animation::new(
id_handle,
duration as _,
AnimationEasing::OutQuad,
get_callback_rect(true),
));
Animation::new(id_handle, duration, AnimationEasing::OutQuad, get_callback_rect(true)).submit(common.alterables);
if let Some(id_label) = id_label {
common.alterables.animate(Animation::new(
id_label,
duration as _,
AnimationEasing::OutQuad,
get_callback_label(true),
));
Animation::new(id_label, duration, AnimationEasing::OutQuad, get_callback_label(true)).submit(common.alterables);
}
}
@@ -499,13 +469,7 @@ fn get_handle_dist(common: &mut CallbackDataCommon, handle: &SliderHandleData, m
const MAX_HOVER_DIST: f32 = 64.0;
fn update_handle_hovers(
common: &mut CallbackDataCommon,
data: &Data,
state: &mut State,
anim_mult: f32,
mouse_pos: Vec2,
) {
fn update_handle_hovers(common: &mut CallbackDataCommon, data: &Data, state: &mut State, mouse_pos: Vec2) {
let hovered1_prev = state.hovered1;
let hovered2_prev = state.hovered2;
@@ -536,9 +500,9 @@ fn update_handle_hovers(
// hover state changed, run animations
if state.hovered1 != hovered1_prev {
if state.hovered1 && !hovered1_prev {
on_enter_anim(common, data.handle1.id_handle_rect, data.handle1.id_label, anim_mult);
on_enter_anim(common, data.handle1.id_handle_rect, data.handle1.id_label);
} else {
on_leave_anim(common, data.handle1.id_handle_rect, data.handle1.id_label, anim_mult);
on_leave_anim(common, data.handle1.id_handle_rect, data.handle1.id_label);
}
}
@@ -546,9 +510,9 @@ fn update_handle_hovers(
&& let Some(handle2) = data.handle2.as_ref()
{
if state.hovered2 && !hovered2_prev {
on_enter_anim(common, handle2.id_handle_rect, handle2.id_label, anim_mult);
on_enter_anim(common, handle2.id_handle_rect, handle2.id_label);
} else {
on_leave_anim(common, handle2.id_handle_rect, handle2.id_label, anim_mult);
on_leave_anim(common, handle2.id_handle_rect, handle2.id_label);
}
}
}
@@ -557,7 +521,6 @@ fn register_event_mouse_motion(
data: Rc<Data>,
state: Rc<RefCell<State>>,
listeners: &mut EventListenerCollection,
anim_mult: f32,
) -> event::EventListenerID {
listeners.register(
EventListenerKind::MouseMotion,
@@ -575,7 +538,7 @@ fn register_event_mouse_motion(
unreachable!();
};
update_handle_hovers(common, &data, &mut state, anim_mult, pos_relative);
update_handle_hovers(common, &data, &mut state, pos_relative);
if let Some(dragged_by) = &state.dragged_by
&& dragged_by.device == pos.device
@@ -769,11 +732,10 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
id: root.id,
lhandles: {
let listeners = &mut root.widget.state().event_listeners;
let anim_mult = ess.layout.state.theme.animation_mult;
vec![
register_event_mouse_enter(state.clone(), listeners, params.tooltip),
register_event_mouse_leave(state.clone(), listeners),
register_event_mouse_motion(data.clone(), state.clone(), listeners, anim_mult),
register_event_mouse_motion(data.clone(), state.clone(), listeners),
register_event_mouse_press(data.clone(), state.clone(), listeners),
register_event_mouse_release(state.clone(), listeners),
]
+25 -30
View File
@@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc};
use taffy::prelude::length;
use crate::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
color::{WguiColor, WguiColorName},
components::{self, Component, ComponentBase, ComponentTrait, RefreshData},
event::CallbackDataCommon,
@@ -292,39 +292,34 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
TooltipSide::Bottom => Vec2::new(0.0, 1.0),
};
let anim_mult = ess.layout.state.theme.animation_mult;
ess.layout.animations.add(Animation::new(
rect.id,
(10.0 * anim_mult) as u32,
AnimationEasing::OutQuad,
{
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap(); /* safe */
let alpha = data.pos;
rect.params.color = rect.params.color.with_alpha(alpha);
rect.params.border_color = rect.params.border_color.with_alpha(alpha);
Animation::new(rect.id, AnimationDuration::Seconds(0.1666), AnimationEasing::OutQuad, {
Box::new(move |common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap(); /* safe */
let alpha = data.pos;
rect.params.color = rect.params.color.with_alpha(alpha);
rect.params.border_color = rect.params.border_color.with_alpha(alpha);
let position_shift = state.borrow().position_shift;
let position_shift = state.borrow().position_shift;
let dir_mult = (1.0 - data.pos) * 5.0;
data.data.transform = Mat4::from_translation(Vec3::new(
direction.x * dir_mult + position_shift.x,
direction.y * dir_mult + position_shift.y,
0.0,
));
let dir_mult = (1.0 - data.pos) * 5.0;
data.data.transform = Mat4::from_translation(Vec3::new(
direction.x * dir_mult + position_shift.x,
direction.y * dir_mult + position_shift.y,
0.0,
));
if let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(label.id) {
label.set_color(
common,
WguiColor::from(WguiColorName::OnBackground).with_alpha(alpha),
true,
);
}
if let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(label.id) {
label.set_color(
common,
WguiColor::from(WguiColorName::OnBackground).with_alpha(alpha),
true,
);
}
common.alterables.mark_redraw();
})
},
));
common.alterables.mark_redraw();
})
})
.submit_l(ess.layout);
ess.layout.defer_component_refresh(Component(tooltip.clone()));
Ok((div, tooltip))
+5 -4
View File
@@ -2,7 +2,7 @@ use image::ImageBuffer;
use taffy::prelude::percent;
use crate::{
animation::Animation,
animation::{Animation, AnimationDuration},
assets::AssetPathRef,
color::WguiColorName,
components::{Component, ComponentBase, ComponentTrait, RefreshData},
@@ -112,10 +112,10 @@ impl ComponentVideo {
let start_time = get_millis();
// log::info!("num frames: {}", source.demuxer.num_frames);
layout.animations.add(Animation::new_ex(
Animation::new_ex(
self.data.id_image,
PLAYBACK_ANIMATION_ID,
u32::MAX, // infinity
AnimationDuration::Infinity,
crate::animation::AnimationEasing::Linear,
Box::new({
let state_ref = self.state.clone();
@@ -163,7 +163,8 @@ impl ComponentVideo {
}
}
}),
));
)
.submit_l(layout);
Ok(())
}
+4 -2
View File
@@ -129,6 +129,7 @@ pub struct LayoutState {
pub nodes: WidgetNodeMap,
pub tree: taffy::tree::TaffyTree<WidgetID>,
pub components_by_widget_id: SecondaryMap<WidgetID, ComponentWeak>,
pub ticks_per_second: u32,
}
pub struct ModifyLayoutStateData<'a> {
@@ -572,6 +573,7 @@ impl Layout {
globals,
theme: params.theme,
components_by_widget_id: SecondaryMap::default(),
ticks_per_second: 60, // hard-coded
};
let size = if params.resize_to_parent {
@@ -755,7 +757,7 @@ impl Layout {
while let Some(task) = tasks.pop_front() {
match task {
LayoutTask::PlayAnimation(animation) => {
self.animations.add(animation);
self.alterables.animate(animation);
}
LayoutTask::RefreshPalette => {
let root = self.tree_root_widget;
@@ -894,7 +896,7 @@ impl Layout {
if !alterables.animations.is_empty() {
self.mark_redraw();
for anim in alterables.animations {
self.animations.add(anim);
self.animations.add(anim, &self.state, &mut self.alterables);
}
}
+1 -1
View File
@@ -14,7 +14,7 @@ impl TooltipAttribs {
pub fn get_info(self) -> Option<tooltip::TooltipInfo> {
self.tooltip.map(|text| tooltip::TooltipInfo {
text,
side: self.tooltip_side.map_or(tooltip::TooltipSide::Top, |f| f),
side: self.tooltip_side.unwrap_or(tooltip::TooltipSide::Top),
})
}
}
+2 -4
View File
@@ -971,10 +971,8 @@ fn process_attribs<'a>(
ctx.doc_params.path.get_str()
);
}
} else {
if let Some(pair) = process_attrib(&file.template_parameters, ctx, key, value) {
res.push(pair);
}
} else if let Some(pair) = process_attrib(&file.template_parameters, ctx, key, value) {
res.push(pair);
}
}
+10 -2
View File
@@ -608,8 +608,16 @@ impl WidgetState {
let mult = scrollbar_info.get_potential_scroll_axis_multiplier(params.taffy_layout);
let scroll_diff_x = if mult.x == 0.0 { 0.0 } else { -mouse_diff.x / mult.x };
let scroll_diff_y = if mult.y == 0.0 { 0.0 } else { -mouse_diff.y / mult.y };
let scroll_diff_x = if mult.x == 0.0 || !active_x {
0.0
} else {
-mouse_diff.x / mult.x
};
let scroll_diff_y = if mult.y == 0.0 || !active_y {
0.0
} else {
-mouse_diff.y / mult.y
};
self.data.scrolling_target = self.data.swipe_scroll_start + Vec2::new(scroll_diff_x, scroll_diff_y);
params.alterables.mark_tick(self.obj.get_id());
+8
View File
@@ -44,10 +44,18 @@ impl WidgetRectangle {
common.mark_widget_dirty(self.id);
}
pub const fn get_color(&self) -> WguiColor {
self.params.color
}
pub fn set_border_color(&mut self, common: &mut CallbackDataCommon, color: WguiColor) {
self.params.border_color = color;
common.mark_widget_dirty(self.id);
}
pub const fn get_border_color(&self) -> WguiColor {
self.params.border_color
}
}
impl WidgetObj for WidgetRectangle {
+14 -4
View File
@@ -4,7 +4,7 @@ use glam::Vec2;
use taffy::prelude::{length, percent};
use crate::{
animation::{Animation, AnimationEasing},
animation::{Animation, AnimationDuration, AnimationEasing},
assets::AssetPathRef,
components::button::ComponentButton,
drawing,
@@ -183,16 +183,17 @@ impl WguiWindow {
)?;
// Fade animation
params.layout.animations.add(Animation::new(
Animation::new(
widget.id,
20,
AnimationDuration::Seconds(0.333),
AnimationEasing::OutQuad,
Box::new(|common, data| {
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap() /* should always succeed */;
rect.params.color = drawing::Color::new(0.0, 0.0, 0.0, data.pos * 0.3).into();
common.alterables.mark_redraw();
}),
));
)
.submit_l(params.layout);
Some(widget)
} else {
@@ -216,6 +217,15 @@ impl WguiWindow {
let globals = params.layout.state.globals.clone();
// window slide animation
Animation::effect_slide(
widget.id,
AnimationDuration::Seconds(0.5),
AnimationEasing::OutQuint,
Vec2::new(0.0, 16.0),
)
.submit_l(params.layout);
let content_id = if params.extra.with_decorations {
let xml_path: AssetPathRef = AssetPathRef::WguiInternal("wgui/window_frame.xml");