crates/gpui/playground/src/components.rs 🔗
@@ -1,5 +1,5 @@
use crate::{
- element::{Element, ElementMetadata},
+ element::{Element, ElementMetadata, ParentElement},
frame,
text::ArcCow,
themes::rose_pine,
Nathan Sobo created
crates/gpui/playground/src/components.rs | 2
crates/gpui/playground/src/element.rs | 40 ++++++++++++++-------
crates/gpui/playground/src/frame.rs | 9 ++--
crates/gpui/playground/src/hoverable.rs | 47 ++++++++++++++++++++++---
crates/gpui/playground/src/playground.rs | 4 +
crates/gpui/src/app.rs | 7 ---
crates/gpui/src/app/window.rs | 6 +++
7 files changed, 82 insertions(+), 33 deletions(-)
@@ -1,5 +1,5 @@
use crate::{
- element::{Element, ElementMetadata},
+ element::{Element, ElementMetadata, ParentElement},
frame,
text::ArcCow,
themes::rose_pine,
@@ -419,7 +419,7 @@ pub trait Element<V: 'static>: 'static {
self
}
- fn hoverable(self) -> Hoverable<V, Self>
+ fn hover(self) -> Hoverable<V, Self>
where
Self: Sized,
{
@@ -443,6 +443,18 @@ pub trait Element<V: 'static>: 'static {
}
}
+pub trait ParentElement<V: 'static>: Element<V> {
+ fn child(self, child: impl IntoElement<V>) -> Self
+ where
+ Self: Sized;
+
+ fn children<I, E>(self, children: I) -> Self
+ where
+ Self: Sized,
+ I: IntoIterator<Item = E>,
+ E: IntoElement<V>;
+}
+
// Object-safe counterpart of Element used by AnyElement to store elements as trait objects.
trait ElementObject<V> {
fn declared_style(&mut self) -> &mut StyleRefinement;
@@ -516,19 +528,6 @@ impl<V: 'static> AnyElement<V> {
Ok(node_id)
}
- pub fn push_text_style<'a: 'b, 'b>(&mut self, cx: &mut impl RenderContext<'a, 'b, V>) -> bool {
- let text_style = self
- .element
- .computed_style(cx.as_view_context())
- .text_style();
- if let Some(text_style) = text_style {
- cx.push_text_style(cx.text_style().refined(&text_style));
- true
- } else {
- false
- }
- }
-
pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
let pushed_text_style = self.push_text_style(cx);
@@ -586,6 +585,19 @@ impl<V: 'static> AnyElement<V> {
Ok(())
}
+
+ fn push_text_style<'a: 'b, 'b>(&mut self, cx: &mut impl RenderContext<'a, 'b, V>) -> bool {
+ let text_style = self
+ .element
+ .computed_style(cx.as_view_context())
+ .text_style();
+ if let Some(text_style) = text_style {
+ cx.push_text_style(cx.text_style().refined(&text_style));
+ true
+ } else {
+ false
+ }
+ }
}
impl<V: 'static> Element<V> for AnyElement<V> {
@@ -1,6 +1,7 @@
use crate::{
element::{
- AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId, PaintContext,
+ AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId,
+ PaintContext, ParentElement,
},
style::{Style, StyleRefinement},
};
@@ -65,13 +66,13 @@ impl<V: 'static> Element<V> for Frame<V> {
}
}
-impl<V: 'static> Frame<V> {
- pub fn child(mut self, child: impl IntoElement<V>) -> Self {
+impl<V: 'static> ParentElement<V> for Frame<V> {
+ fn child(mut self, child: impl IntoElement<V>) -> Self {
self.children.push(child.into_any_element());
self
}
- pub fn children<I, E>(mut self, children: I) -> Self
+ fn children<I, E>(mut self, children: I) -> Self
where
I: IntoIterator<Item = E>,
E: IntoElement<V>,
@@ -7,11 +7,15 @@ use gpui::{
};
use refineable::Refineable;
-use crate::{element::Element, style::StyleRefinement};
+use crate::{
+ element::{Element, ParentElement},
+ style::StyleRefinement,
+};
pub struct Hoverable<V, E> {
hover_style: StyleRefinement,
computed_style: Option<StyleRefinement>,
+ hovered: Rc<Cell<bool>>,
view_type: PhantomData<V>,
child: E,
}
@@ -21,6 +25,7 @@ impl<V, E> Hoverable<V, E> {
Self {
hover_style: StyleRefinement::default(),
computed_style: None,
+ hovered: Default::default(),
view_type: PhantomData,
child,
}
@@ -37,8 +42,9 @@ impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
self.computed_style.get_or_insert_with(|| {
let mut style = self.child.computed_style(cx).clone();
- // if hovered
- style.refine(&self.hover_style);
+ if self.hovered.get() {
+ style.refine(&self.hover_style);
+ }
style
})
}
@@ -63,17 +69,24 @@ impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
) -> anyhow::Result<()> {
let EngineLayout { bounds, order } = layout.from_engine;
let window_bounds = RectF::new(Vector2F::zero(), cx.window_size());
- let was_hovered = Rc::new(Cell::new(false));
+ let hovered = self.hovered.clone();
self.child.paint(layout, view, cx)?;
+
+ let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
+ if mouse_within_bounds != hovered.get() {
+ hovered.set(mouse_within_bounds);
+ cx.repaint();
+ }
+
cx.draw_interactive_region(
order,
window_bounds,
false,
move |view, event: &MouseMove, cx| {
- let is_hovered = bounds.contains_point(cx.mouse_position());
- if is_hovered != was_hovered.get() {
- was_hovered.set(is_hovered);
+ let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
+ if mouse_within_bounds != hovered.get() {
+ hovered.set(mouse_within_bounds);
cx.repaint();
}
},
@@ -81,3 +94,23 @@ impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
Ok(())
}
}
+
+impl<V: 'static, P: ParentElement<V>> ParentElement<V> for Hoverable<V, P> {
+ fn child(mut self, child: impl crate::element::IntoElement<V>) -> Self
+ where
+ Self: Sized,
+ {
+ self.child = self.child.child(child);
+ self
+ }
+
+ fn children<I, E>(mut self, children: I) -> Self
+ where
+ Self: Sized,
+ I: IntoIterator<Item = E>,
+ E: crate::element::IntoElement<V>,
+ {
+ self.child = self.child.children(children);
+ self
+ }
+}
@@ -1,7 +1,7 @@
#![allow(dead_code, unused_variables)]
use color::black;
use components::button;
-use element::Element;
+use element::{Element, ParentElement};
use frame::frame;
use gpui::{
geometry::{rect::RectF, vector::vec2f},
@@ -50,6 +50,8 @@ fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
.h_full()
.w_half()
.fill(theme.success(0.5))
+ .hover()
+ .fill(theme.error(0.5))
.child(button().label("Hello").click(|_, _, _| println!("click!")))
}
@@ -1890,6 +1890,7 @@ impl AppContext {
fn handle_repaint_window_effect(&mut self, window: AnyWindowHandle) {
self.update_window(window, |cx| {
+ cx.layout(false).log_err();
if let Some(scene) = cx.paint().log_err() {
cx.window.platform_window.present_scene(scene);
}
@@ -3655,12 +3656,6 @@ impl<'a, 'b, 'c, V: 'static> EventContext<'a, 'b, 'c, V> {
pub fn propagate_event(&mut self) {
self.handled = false;
}
-
- pub fn repaint(&mut self) {
- let window = self.window();
- self.pending_effects
- .push_back(Effect::RepaintWindow { window });
- }
}
impl<'a, 'b, 'c, V> Deref for EventContext<'a, 'b, 'c, V> {
@@ -220,6 +220,12 @@ impl<'a> WindowContext<'a> {
}
}
+ pub fn repaint(&mut self) {
+ let window = self.window();
+ self.pending_effects
+ .push_back(Effect::RepaintWindow { window });
+ }
+
pub fn layout_engine(&mut self) -> Option<&mut LayoutEngine> {
self.window.layout_engines.last_mut()
}