@@ -1,10 +1,9 @@
-use std::{fs, path::PathBuf, time::Duration};
+use std::{fs, path::PathBuf};
use anyhow::Result;
use gpui::{
App, Application, AssetSource, Bounds, BoxShadow, ClickEvent, Context, SharedString, Task,
- Timer, Window, WindowBounds, WindowOptions, div, hsla, img, point, prelude::*, px, rgb, size,
- svg,
+ Window, WindowBounds, WindowOptions, div, hsla, img, point, prelude::*, px, rgb, size, svg,
};
struct Assets {
@@ -37,6 +36,7 @@ impl AssetSource for Assets {
struct HelloWorld {
_task: Option<Task<()>>,
opacity: f32,
+ animating: bool,
}
impl HelloWorld {
@@ -44,39 +44,29 @@ impl HelloWorld {
Self {
_task: None,
opacity: 0.5,
+ animating: false,
}
}
- fn change_opacity(&mut self, _: &ClickEvent, window: &mut Window, cx: &mut Context<Self>) {
+ fn start_animation(&mut self, _: &ClickEvent, _: &mut Window, cx: &mut Context<Self>) {
self.opacity = 0.0;
+ self.animating = true;
cx.notify();
-
- self._task = Some(cx.spawn_in(window, async move |view, cx| {
- loop {
- Timer::after(Duration::from_secs_f32(0.05)).await;
- let mut stop = false;
- let _ = cx.update(|_, cx| {
- view.update(cx, |view, cx| {
- if view.opacity >= 1.0 {
- stop = true;
- return;
- }
-
- view.opacity += 0.1;
- cx.notify();
- })
- });
-
- if stop {
- break;
- }
- }
- }));
}
}
impl Render for HelloWorld {
- fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ if self.animating {
+ self.opacity += 0.005;
+ if self.opacity >= 1.0 {
+ self.animating = false;
+ self.opacity = 1.0;
+ } else {
+ window.request_animation_frame();
+ }
+ }
+
div()
.flex()
.flex_row()
@@ -96,7 +86,7 @@ impl Render for HelloWorld {
.child(
div()
.id("panel")
- .on_click(cx.listener(Self::change_opacity))
+ .on_click(cx.listener(Self::start_animation))
.absolute()
.top_8()
.left_8()
@@ -150,7 +140,15 @@ impl Render for HelloWorld {
.text_2xl()
.size_8(),
)
- .child("🎊✈️🎉🎈🎁🎂")
+ .child(
+ div()
+ .flex()
+ .children(["🎊", "✈️", "🎉", "🎈", "🎁", "🎂"].map(|emoji| {
+ div()
+ .child(emoji.to_string())
+ .hover(|style| style.opacity(0.5))
+ })),
+ )
.child(img("image/black-cat-typing.gif").size_12()),
),
)
@@ -837,7 +837,7 @@ pub struct Window {
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
pub(crate) rendered_entity_stack: Vec<EntityId>,
pub(crate) element_offset_stack: Vec<Point<Pixels>>,
- pub(crate) element_opacity: Option<f32>,
+ pub(crate) element_opacity: f32,
pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
pub(crate) requested_autoscroll: Option<Bounds<Pixels>>,
pub(crate) image_cache_stack: Vec<AnyImageCache>,
@@ -1222,7 +1222,7 @@ impl Window {
rendered_entity_stack: Vec::new(),
element_offset_stack: Vec::new(),
content_mask_stack: Vec::new(),
- element_opacity: None,
+ element_opacity: 1.0,
requested_autoscroll: None,
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
@@ -2435,14 +2435,16 @@ impl Window {
opacity: Option<f32>,
f: impl FnOnce(&mut Self) -> R,
) -> R {
- if opacity.is_none() {
+ self.invalidator.debug_assert_paint_or_prepaint();
+
+ let Some(opacity) = opacity else {
return f(self);
- }
+ };
- self.invalidator.debug_assert_paint_or_prepaint();
- self.element_opacity = opacity;
+ let previous_opacity = self.element_opacity;
+ self.element_opacity = previous_opacity * opacity;
let result = f(self);
- self.element_opacity = None;
+ self.element_opacity = previous_opacity;
result
}
@@ -2539,9 +2541,10 @@ impl Window {
/// Obtain the current element opacity. This method should only be called during the
/// prepaint phase of element drawing.
+ #[inline]
pub(crate) fn element_opacity(&self) -> f32 {
self.invalidator.debug_assert_paint_or_prepaint();
- self.element_opacity.unwrap_or(1.0)
+ self.element_opacity
}
/// Obtain the current content mask. This method should only be called during element drawing.