Compare commits

...

3 Commits

Author SHA1 Message Date
Nate Butler
d1711f91f6 wip 2025-05-17 07:39:54 -04:00
Nate Butler
8fd2316deb Continue on basic button 2025-05-16 18:10:26 +02:00
Nate Butler
7357796969 WIP gpui button 2025-05-16 17:47:51 +02:00
4 changed files with 204 additions and 5 deletions

View File

@@ -1,14 +1,15 @@
use gpui::{
App, Application, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, div,
prelude::*, px, rgb, size,
App, Application, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, button,
div, prelude::*, px, rgb, size,
};
struct HelloWorld {
text: SharedString,
count: usize,
}
impl Render for HelloWorld {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.flex()
.flex_col()
@@ -23,6 +24,14 @@ impl Render for HelloWorld {
.text_xl()
.text_color(rgb(0xffffff))
.child(format!("Hello, {}!", &self.text))
.child(
button("counter-button")
.on_click(Box::new(cx.listener(move |this, _, _, cx| {
this.count += 1;
cx.notify();
})))
.child(div().child(format!("Count: {}", &self.count))),
)
.child(
div()
.flex()
@@ -97,6 +106,7 @@ fn main() {
|_, cx| {
cx.new(|_| HelloWorld {
text: "World".into(),
count: 0,
})
},
)

View File

@@ -0,0 +1,187 @@
#![allow(missing_docs)]
use super::{FocusableElement, InteractiveElement, Interactivity, StatefulInteractiveElement};
use crate::{
AbsoluteLength, AnyElement, App, BorderStyle, ClickEvent, CornersRefinement, Edges,
EdgesRefinement, Element, ElementId, GlobalElementId, Hitbox, IntoElement, LayoutId,
ParentElement, SharedString, StyleRefinement, Styled, TextStyleRefinement, Window,
colors::Colors, px,
};
use refineable::Refineable;
use smallvec::SmallVec;
use taffy::FlexDirection;
use util::default;
pub fn button(id: impl Into<ElementId>) -> Button {
Button {
id: id.into(),
interactivity: Interactivity::default(),
children: SmallVec::new(),
}
}
pub struct Button {
id: ElementId,
interactivity: Interactivity,
children: SmallVec<[AnyElement; 2]>,
}
impl Element for Button {
type RequestLayoutState = ();
type PrepaintState = Option<Hitbox>;
fn id(&self) -> Option<ElementId> {
Some(self.id.clone())
}
fn request_layout(
&mut self,
global_id: Option<&GlobalElementId>,
window: &mut Window,
cx: &mut App,
) -> (LayoutId, Self::RequestLayoutState) {
// Get a LayoutId, an identifier Taffy uses to indicate a unique layout element
let layout_id =
self.interactivity
.request_layout(global_id, window, cx, |style, window, cx| {
let mut child_layout_ids = Vec::new();
for child in &mut self.children {
let child_layout_id = child.request_layout(window, cx);
child_layout_ids.push(child_layout_id);
}
window.request_layout(style, child_layout_ids, cx)
});
// Initialize the layout state
let layout_state = ();
(layout_id, layout_state)
}
fn prepaint(
&mut self,
global_id: Option<&crate::GlobalElementId>,
bounds: crate::Bounds<crate::Pixels>,
_request_layout: &mut Self::RequestLayoutState,
window: &mut Window,
cx: &mut App,
) -> Self::PrepaintState {
if let Some(handle) = self.interactivity.scroll_anchor.as_ref() {
*handle.last_origin.borrow_mut() = bounds.origin - window.element_offset();
}
let content_size = bounds.size;
// Prepaint children
for child in &mut self.children {
child.prepaint(window, cx);
}
self.interactivity.prepaint(
global_id,
bounds,
content_size,
window,
cx,
|_style, _scroll_offset, hitbox, _window, _cx| hitbox,
)
}
fn paint(
&mut self,
global_id: Option<&crate::GlobalElementId>,
bounds: crate::Bounds<crate::Pixels>,
_request_layout: &mut Self::RequestLayoutState,
hitbox: &mut Option<Hitbox>,
window: &mut Window,
cx: &mut App,
) {
let colors = Colors::for_appearance(window);
let text_style = self.text_style().clone();
let abs_px = |number: f32| AbsoluteLength::Pixels(px(number));
let mut text_style = if let Some(style) = text_style {
style.clone()
} else {
TextStyleRefinement::default()
};
let new_style = StyleRefinement {
background: Some(colors.container.into()),
text: Some(TextStyleRefinement {
color: Some(colors.text.into()),
font_size: Some(px(13.).into()),
..text_style
}),
border_color: Some(colors.border.into()),
border_style: Some(BorderStyle::Solid),
border_widths: EdgesRefinement {
top: Some(abs_px(1.)),
right: Some(abs_px(1.)),
bottom: Some(abs_px(1.)),
left: Some(abs_px(1.)),
},
corner_radii: CornersRefinement {
top_left: Some(abs_px(4.)),
top_right: Some(abs_px(4.)),
bottom_left: Some(abs_px(4.)),
bottom_right: Some(abs_px(4.)),
},
flex_direction: Some(FlexDirection::Row),
flex_grow: Some(0.),
..StyleRefinement::default()
};
let refined = self.style().refine(&new_style);
self.interactivity.paint(
global_id,
bounds,
hitbox.as_ref(),
window,
cx,
|style, window, cx| {
for child in &mut self.children {
child.paint(window, cx);
}
},
)
}
}
impl IntoElement for Button {
type Element = Self;
fn into_element(self) -> Self::Element {
self
}
}
impl Styled for Button {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.interactivity.base_style
}
}
impl InteractiveElement for Button {
fn interactivity(&mut self) -> &mut Interactivity {
&mut self.interactivity
}
}
impl StatefulInteractiveElement for Button {}
impl FocusableElement for Button {}
impl ParentElement for Button {
fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
self.children.extend(elements)
}
}
impl Button {
pub fn on_click(
mut self,
callback: Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>,
) -> Self {
self.interactivity
.on_click(move |event, window, cx| callback(event, window, cx));
self
}
}

View File

@@ -2877,8 +2877,8 @@ where
/// Contrary to [ScrollHandle::scroll_to_item], an anchored element does not have to be an immediate child of the parent.
#[derive(Clone)]
pub struct ScrollAnchor {
handle: ScrollHandle,
last_origin: Rc<RefCell<Point<Pixels>>>,
pub(super) handle: ScrollHandle,
pub(super) last_origin: Rc<RefCell<Point<Pixels>>>,
}
impl ScrollAnchor {

View File

@@ -1,5 +1,6 @@
mod anchored;
mod animation;
mod button;
mod canvas;
mod deferred;
mod div;
@@ -13,6 +14,7 @@ mod uniform_list;
pub use anchored::*;
pub use animation::*;
pub use button::*;
pub use canvas::*;
pub use deferred::*;
pub use div::*;