wip: picker

co-authored-by: nathan <nathan@zed.dev>
co-authored-by: max <max@zed.dev>
This commit is contained in:
Mikayla 2023-11-06 17:09:38 -08:00
parent 3c93b585ab
commit 85000eba81
No known key found for this signature in database
21 changed files with 460 additions and 278 deletions

4
Cargo.lock generated
View File

@ -4970,7 +4970,8 @@ dependencies = [
name = "menu2"
version = "0.1.0"
dependencies = [
"gpui2",
"serde",
"serde_derive",
]
[[package]]
@ -8542,6 +8543,7 @@ dependencies = [
"gpui2",
"itertools 0.11.0",
"log",
"menu2",
"picker2",
"rust-embed",
"serde",

View File

@ -194,6 +194,15 @@ pub fn red() -> Hsla {
}
}
pub fn blue() -> Hsla {
Hsla {
h: 0.6,
s: 1.,
l: 0.5,
a: 1.,
}
}
impl Hsla {
/// Returns true if the HSLA color is fully transparent, false otherwise.
pub fn is_transparent(&self) -> bool {

View File

@ -1,28 +1,28 @@
use crate::{
point, AnyElement, BorrowWindow, Bounds, Component, Element, ElementFocus, ElementId,
ElementInteraction, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable,
ElementInteractivity, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable,
GlobalElementId, GroupBounds, InteractiveElementState, LayoutId, Overflow, ParentElement,
Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, Visibility,
Pixels, Point, SharedString, StatefulInteractive, StatefulInteractivity, StatelessInteractive,
StatelessInteractivity, Style, StyleRefinement, Styled, ViewContext, Visibility,
};
use refineable::Refineable;
use smallvec::SmallVec;
pub struct Div<
V: 'static,
I: ElementInteraction<V> = StatelessInteraction<V>,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: ElementFocus<V> = FocusDisabled,
> {
interaction: I,
interactivity: I,
focus: F,
children: SmallVec<[AnyElement<V>; 2]>,
group: Option<SharedString>,
base_style: StyleRefinement,
}
pub fn div<V: 'static>() -> Div<V, StatelessInteraction<V>, FocusDisabled> {
pub fn div<V: 'static>() -> Div<V, StatelessInteractivity<V>, FocusDisabled> {
Div {
interaction: StatelessInteraction::default(),
interactivity: StatelessInteractivity::default(),
focus: FocusDisabled,
children: SmallVec::new(),
group: None,
@ -30,14 +30,14 @@ pub fn div<V: 'static>() -> Div<V, StatelessInteraction<V>, FocusDisabled> {
}
}
impl<V, F> Div<V, StatelessInteraction<V>, F>
impl<V, F> Div<V, StatelessInteractivity<V>, F>
where
V: 'static,
F: ElementFocus<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Div<V, StatefulInteraction<V>, F> {
pub fn id(self, id: impl Into<ElementId>) -> Div<V, StatefulInteractivity<V>, F> {
Div {
interaction: id.into().into(),
interactivity: id.into().into(),
focus: self.focus,
children: self.children,
group: self.group,
@ -48,7 +48,7 @@ where
impl<V, I, F> Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
pub fn group(mut self, group: impl Into<SharedString>) -> Self {
@ -98,16 +98,20 @@ where
let mut computed_style = Style::default();
computed_style.refine(&self.base_style);
self.focus.refine_style(&mut computed_style, cx);
self.interaction
.refine_style(&mut computed_style, bounds, &element_state.interactive, cx);
self.interactivity.refine_style(
&mut computed_style,
bounds,
&element_state.interactive,
cx,
);
computed_style
}
}
impl<V: 'static> Div<V, StatefulInteraction<V>, FocusDisabled> {
pub fn focusable(self) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
impl<V: 'static> Div<V, StatefulInteractivity<V>, FocusDisabled> {
pub fn focusable(self) -> Div<V, StatefulInteractivity<V>, FocusEnabled<V>> {
Div {
interaction: self.interaction,
interactivity: self.interactivity,
focus: FocusEnabled::new(),
children: self.children,
group: self.group,
@ -118,9 +122,9 @@ impl<V: 'static> Div<V, StatefulInteraction<V>, FocusDisabled> {
pub fn track_focus(
self,
handle: &FocusHandle,
) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
) -> Div<V, StatefulInteractivity<V>, FocusEnabled<V>> {
Div {
interaction: self.interaction,
interactivity: self.interactivity,
focus: FocusEnabled::tracked(handle),
children: self.children,
group: self.group,
@ -145,13 +149,13 @@ impl<V: 'static> Div<V, StatefulInteraction<V>, FocusDisabled> {
}
}
impl<V: 'static> Div<V, StatelessInteraction<V>, FocusDisabled> {
impl<V: 'static> Div<V, StatelessInteractivity<V>, FocusDisabled> {
pub fn track_focus(
self,
handle: &FocusHandle,
) -> Div<V, StatefulInteraction<V>, FocusEnabled<V>> {
) -> Div<V, StatefulInteractivity<V>, FocusEnabled<V>> {
Div {
interaction: self.interaction.into_stateful(handle),
interactivity: self.interactivity.into_stateful(handle),
focus: handle.clone().into(),
children: self.children,
group: self.group,
@ -163,7 +167,7 @@ impl<V: 'static> Div<V, StatelessInteraction<V>, FocusDisabled> {
impl<V, I> Focusable<V> for Div<V, I, FocusEnabled<V>>
where
V: 'static,
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
&mut self.focus.focus_listeners
@ -191,13 +195,13 @@ pub struct DivState {
impl<V, I, F> Element<V> for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
type ElementState = DivState;
fn id(&self) -> Option<ElementId> {
self.interaction
self.interactivity
.as_stateful()
.map(|identified| identified.id.clone())
}
@ -212,7 +216,7 @@ where
self.focus
.initialize(element_state.focus_handle.take(), cx, |focus_handle, cx| {
element_state.focus_handle = focus_handle;
self.interaction.initialize(cx, |cx| {
self.interactivity.initialize(cx, |cx| {
for child in &mut self.children {
child.initialize(view_state, cx);
}
@ -281,11 +285,11 @@ where
(child_max - child_min).into()
};
cx.stack(z_index, |cx| {
cx.stack(0, |cx| {
cx.with_z_index(z_index, |cx| {
cx.with_z_index(0, |cx| {
style.paint(bounds, cx);
this.focus.paint(bounds, cx);
this.interaction.paint(
this.interactivity.paint(
bounds,
content_size,
style.overflow,
@ -293,7 +297,7 @@ where
cx,
);
});
cx.stack(1, |cx| {
cx.with_z_index(1, |cx| {
style.apply_text_style(cx, |cx| {
style.apply_overflow(bounds, cx, |cx| {
let scroll_offset = element_state.interactive.scroll_offset();
@ -316,7 +320,7 @@ where
impl<V, I, F> Component<V> for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn render(self) -> AnyElement<V> {
@ -326,7 +330,7 @@ where
impl<V, I, F> ParentElement<V> for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
@ -336,7 +340,7 @@ where
impl<V, I, F> Styled for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn style(&mut self) -> &mut StyleRefinement {
@ -346,19 +350,19 @@ where
impl<V, I, F> StatelessInteractive<V> for Div<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V> {
self.interaction.as_stateless_mut()
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.interactivity.as_stateless_mut()
}
}
impl<V, F> StatefulInteractive<V> for Div<V, StatefulInteraction<V>, F>
impl<V, F> StatefulInteractive<V> for Div<V, StatefulInteractivity<V>, F>
where
F: ElementFocus<V>,
{
fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V> {
&mut self.interaction
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
&mut self.interactivity
}
}

View File

@ -1,17 +1,15 @@
use std::sync::Arc;
use crate::{
div, AnyElement, BorrowWindow, Bounds, Component, Div, DivState, Element, ElementFocus,
ElementId, ElementInteraction, FocusDisabled, FocusEnabled, FocusListeners, Focusable,
LayoutId, Pixels, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
StatelessInteractive, StyleRefinement, Styled, ViewContext,
ElementId, ElementInteractivity, FocusDisabled, FocusEnabled, FocusListeners, Focusable,
LayoutId, Pixels, SharedString, StatefulInteractive, StatefulInteractivity,
StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled, ViewContext,
};
use futures::FutureExt;
use util::ResultExt;
pub struct Img<
V: 'static,
I: ElementInteraction<V> = StatelessInteraction<V>,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: ElementFocus<V> = FocusDisabled,
> {
base: Div<V, I, F>,
@ -19,7 +17,7 @@ pub struct Img<
grayscale: bool,
}
pub fn img<V: 'static>() -> Img<V, StatelessInteraction<V>, FocusDisabled> {
pub fn img<V: 'static>() -> Img<V, StatelessInteractivity<V>, FocusDisabled> {
Img {
base: div(),
uri: None,
@ -30,7 +28,7 @@ pub fn img<V: 'static>() -> Img<V, StatelessInteraction<V>, FocusDisabled> {
impl<V, I, F> Img<V, I, F>
where
V: 'static,
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
pub fn uri(mut self, uri: impl Into<SharedString>) -> Self {
@ -44,11 +42,11 @@ where
}
}
impl<V, F> Img<V, StatelessInteraction<V>, F>
impl<V, F> Img<V, StatelessInteractivity<V>, F>
where
F: ElementFocus<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Img<V, StatefulInteraction<V>, F> {
pub fn id(self, id: impl Into<ElementId>) -> Img<V, StatefulInteractivity<V>, F> {
Img {
base: self.base.id(id),
uri: self.uri,
@ -59,7 +57,7 @@ where
impl<V, I, F> Component<V> for Img<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn render(self) -> AnyElement<V> {
@ -69,7 +67,7 @@ where
impl<V, I, F> Element<V> for Img<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
type ElementState = DivState;
@ -103,7 +101,7 @@ where
element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) {
cx.stack(0, |cx| {
cx.with_z_index(0, |cx| {
self.base.paint(bounds, view, element_state, cx);
});
@ -120,7 +118,7 @@ where
.and_then(ResultExt::log_err)
{
let corner_radii = corner_radii.to_pixels(bounds.size, cx.rem_size());
cx.stack(1, |cx| {
cx.with_z_index(1, |cx| {
cx.paint_image(bounds, corner_radii, data, self.grayscale)
.log_err()
});
@ -138,7 +136,7 @@ where
impl<V, I, F> Styled for Img<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn style(&mut self) -> &mut StyleRefinement {
@ -148,27 +146,27 @@ where
impl<V, I, F> StatelessInteractive<V> for Img<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V> {
self.base.stateless_interaction()
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Img<V, StatefulInteraction<V>, F>
impl<V, F> StatefulInteractive<V> for Img<V, StatefulInteractivity<V>, F>
where
F: ElementFocus<V>,
{
fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V> {
self.base.stateful_interaction()
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V, I> Focusable<V> for Img<V, I, FocusEnabled<V>>
where
V: 'static,
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()

View File

@ -1,15 +1,12 @@
use std::{cmp, ops::Range};
use smallvec::SmallVec;
use crate::{
point, px, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
LayoutId, Pixels, Size, StyleRefinement, Styled, ViewContext,
ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Size, StatefulInteractive,
StatefulInteractivity, StatelessInteractive, StatelessInteractivity, StyleRefinement, Styled,
ViewContext,
};
// We want to support uniform and non-uniform height
// We need to make the ID mandatory, to replace the 'state' field
// Previous implementation measured the first element as early as possible
use smallvec::SmallVec;
use std::{cmp, ops::Range};
use taffy::style::Overflow;
pub fn list<Id, V, C>(
id: Id,
@ -21,8 +18,9 @@ where
V: 'static,
C: Component<V>,
{
let id = id.into();
List {
id: id.into(),
id: id.clone(),
style: Default::default(),
item_count,
render_items: Box::new(move |view, visible_range, cx| {
@ -31,10 +29,11 @@ where
.map(|component| component.render())
.collect()
}),
interactivity: id.into(),
}
}
pub struct List<V> {
pub struct List<V: 'static> {
id: ElementId,
style: StyleRefinement,
item_count: usize,
@ -45,19 +44,12 @@ pub struct List<V> {
&'a mut ViewContext<V>,
) -> SmallVec<[AnyElement<V>; 64]>,
>,
interactivity: StatefulInteractivity<V>,
}
// #[derive(Debug)]
// pub enum ScrollTarget {
// Show(usize),
// Center(usize),
// }
#[derive(Default)]
pub struct ListState {
scroll_top: f32,
// todo
// scroll_to: Option<ScrollTarget>,
interactive: InteractiveElementState,
}
impl<V: 'static> Styled for List<V> {
@ -111,30 +103,66 @@ impl<V: 'static> Element<V> for List<V> {
- point(border.right + padding.right, border.bottom + padding.bottom),
);
if self.item_count > 0 {
let item_height = self.measure_item_height(view_state, padded_bounds, cx);
let visible_item_count = (padded_bounds.size.height / item_height).ceil() as usize;
let visible_range = 0..cmp::min(visible_item_count, self.item_count);
cx.with_z_index(style.z_index.unwrap_or(0), |cx| {
let content_size;
if self.item_count > 0 {
let item_height = self.measure_item_height(view_state, padded_bounds, cx);
let visible_item_count =
(padded_bounds.size.height / item_height).ceil() as usize + 1;
let scroll_offset = element_state
.interactive
.scroll_offset()
.map_or((0.0).into(), |offset| offset.y);
let first_visible_element_ix = (-scroll_offset / item_height).floor() as usize;
let visible_range = first_visible_element_ix
..cmp::min(
first_visible_element_ix + visible_item_count,
self.item_count,
);
let mut items = (self.render_items)(view_state, visible_range, cx);
let mut items = (self.render_items)(view_state, visible_range.clone(), cx);
dbg!(items.len(), self.item_count, visible_item_count);
content_size = Size {
width: padded_bounds.size.width,
height: item_height * self.item_count,
};
for (ix, item) in items.iter_mut().enumerate() {
item.initialize(view_state, cx);
cx.with_z_index(1, |cx| {
for (item, ix) in items.iter_mut().zip(visible_range) {
item.initialize(view_state, cx);
let layout_id = item.layout(view_state, cx);
cx.compute_layout(
layout_id,
Size {
width: AvailableSpace::Definite(bounds.size.width),
height: AvailableSpace::Definite(item_height),
},
);
let offset = padded_bounds.origin + point(px(0.), item_height * ix);
cx.with_element_offset(Some(offset), |cx| item.paint(view_state, cx))
let layout_id = item.layout(view_state, cx);
cx.compute_layout(
layout_id,
Size {
width: AvailableSpace::Definite(bounds.size.width),
height: AvailableSpace::Definite(item_height),
},
);
let offset =
padded_bounds.origin + point(px(0.), item_height * ix + scroll_offset);
cx.with_element_offset(Some(offset), |cx| item.paint(view_state, cx))
}
});
} else {
content_size = Size {
width: bounds.size.width,
height: px(0.),
};
}
}
let overflow = point(style.overflow.x, Overflow::Scroll);
cx.with_z_index(0, |cx| {
self.interactivity.paint(
bounds,
content_size,
overflow,
&mut element_state.interactive,
cx,
);
});
})
}
}
@ -161,6 +189,18 @@ impl<V> List<V> {
}
}
impl<V: 'static> StatelessInteractive<V> for List<V> {
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.interactivity.as_stateless_mut()
}
}
impl<V: 'static> StatefulInteractive<V> for List<V> {
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
&mut self.interactivity
}
}
impl<V: 'static> Component<V> for List<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)

View File

@ -1,21 +1,21 @@
use crate::{
div, AnyElement, Bounds, Component, Div, DivState, Element, ElementFocus, ElementId,
ElementInteraction, FocusDisabled, FocusEnabled, FocusListeners, Focusable, LayoutId, Pixels,
SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction,
StatelessInteractive, StyleRefinement, Styled, ViewContext,
ElementInteractivity, FocusDisabled, FocusEnabled, FocusListeners, Focusable, LayoutId, Pixels,
SharedString, StatefulInteractive, StatefulInteractivity, StatelessInteractive,
StatelessInteractivity, StyleRefinement, Styled, ViewContext,
};
use util::ResultExt;
pub struct Svg<
V: 'static,
I: ElementInteraction<V> = StatelessInteraction<V>,
I: ElementInteractivity<V> = StatelessInteractivity<V>,
F: ElementFocus<V> = FocusDisabled,
> {
base: Div<V, I, F>,
path: Option<SharedString>,
}
pub fn svg<V: 'static>() -> Svg<V, StatelessInteraction<V>, FocusDisabled> {
pub fn svg<V: 'static>() -> Svg<V, StatelessInteractivity<V>, FocusDisabled> {
Svg {
base: div(),
path: None,
@ -24,7 +24,7 @@ pub fn svg<V: 'static>() -> Svg<V, StatelessInteraction<V>, FocusDisabled> {
impl<V, I, F> Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
@ -33,11 +33,11 @@ where
}
}
impl<V, F> Svg<V, StatelessInteraction<V>, F>
impl<V, F> Svg<V, StatelessInteractivity<V>, F>
where
F: ElementFocus<V>,
{
pub fn id(self, id: impl Into<ElementId>) -> Svg<V, StatefulInteraction<V>, F> {
pub fn id(self, id: impl Into<ElementId>) -> Svg<V, StatefulInteractivity<V>, F> {
Svg {
base: self.base.id(id),
path: self.path,
@ -47,7 +47,7 @@ where
impl<V, I, F> Component<V> for Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn render(self) -> AnyElement<V> {
@ -57,7 +57,7 @@ where
impl<V, I, F> Element<V> for Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
type ElementState = DivState;
@ -107,7 +107,7 @@ where
impl<V, I, F> Styled for Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn style(&mut self) -> &mut StyleRefinement {
@ -117,27 +117,27 @@ where
impl<V, I, F> StatelessInteractive<V> for Svg<V, I, F>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
F: ElementFocus<V>,
{
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V> {
self.base.stateless_interaction()
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
self.base.stateless_interactivity()
}
}
impl<V, F> StatefulInteractive<V> for Svg<V, StatefulInteraction<V>, F>
impl<V, F> StatefulInteractive<V> for Svg<V, StatefulInteractivity<V>, F>
where
V: 'static,
F: ElementFocus<V>,
{
fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V> {
self.base.stateful_interaction()
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
self.base.stateful_interactivity()
}
}
impl<V: 'static, I> Focusable<V> for Svg<V, I, FocusEnabled<V>>
where
I: ElementInteraction<V>,
I: ElementInteractivity<V>,
{
fn focus_listeners(&mut self) -> &mut FocusListeners<V> {
self.base.focus_listeners()

View File

@ -25,13 +25,13 @@ const TOOLTIP_DELAY: Duration = Duration::from_millis(500);
const TOOLTIP_OFFSET: Point<Pixels> = Point::new(px(10.0), px(8.0));
pub trait StatelessInteractive<V: 'static>: Element<V> {
fn stateless_interaction(&mut self) -> &mut StatelessInteraction<V>;
fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V>;
fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.stateless_interaction().hover_style = f(StyleRefinement::default());
self.stateless_interactivity().hover_style = f(StyleRefinement::default());
self
}
@ -43,7 +43,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().group_hover_style = Some(GroupStyle {
self.stateless_interactivity().group_hover_style = Some(GroupStyle {
group: group_name.into(),
style: f(StyleRefinement::default()),
});
@ -58,7 +58,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_down_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble
@ -79,7 +79,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_up_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble
@ -100,7 +100,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_down_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture
@ -121,7 +121,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_up_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture
@ -141,7 +141,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.mouse_move_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
@ -158,7 +158,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.scroll_wheel_listeners
.push(Box::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
@ -174,23 +174,48 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
C: TryInto<DispatchContext>,
C::Error: Debug,
{
self.stateless_interaction().dispatch_context =
self.stateless_interactivity().dispatch_context =
context.try_into().expect("invalid dispatch context");
self
}
fn on_action<A: 'static>(
/// Capture the given action, fires during the capture phase
fn capture_action<A: 'static>(
mut self,
listener: impl Fn(&mut V, &A, DispatchPhase, &mut ViewContext<V>) + 'static,
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.stateless_interaction().key_listeners.push((
self.stateless_interactivity().key_listeners.push((
TypeId::of::<A>(),
Box::new(move |view, event, _, phase, cx| {
let event = event.downcast_ref().unwrap();
listener(view, event, phase, cx);
if phase == DispatchPhase::Capture {
listener(view, event, cx)
}
None
}),
));
self
}
/// Add a listener for the given action, fires during the bubble event phase
fn on_action<A: 'static>(
mut self,
listener: impl Fn(&mut V, &A, &mut ViewContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.stateless_interactivity().key_listeners.push((
TypeId::of::<A>(),
Box::new(move |view, event, _, phase, cx| {
let event = event.downcast_ref().unwrap();
if phase == DispatchPhase::Bubble {
listener(view, event, cx)
}
None
}),
));
@ -204,7 +229,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().key_listeners.push((
self.stateless_interactivity().key_listeners.push((
TypeId::of::<KeyDownEvent>(),
Box::new(move |view, event, _, phase, cx| {
let event = event.downcast_ref().unwrap();
@ -222,7 +247,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().key_listeners.push((
self.stateless_interactivity().key_listeners.push((
TypeId::of::<KeyUpEvent>(),
Box::new(move |view, event, _, phase, cx| {
let event = event.downcast_ref().unwrap();
@ -237,7 +262,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction()
self.stateless_interactivity()
.drag_over_styles
.push((TypeId::of::<S>(), f(StyleRefinement::default())));
self
@ -251,7 +276,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().group_drag_over_styles.push((
self.stateless_interactivity().group_drag_over_styles.push((
TypeId::of::<S>(),
GroupStyle {
group: group_name.into(),
@ -268,7 +293,7 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
where
Self: Sized,
{
self.stateless_interaction().drop_listeners.push((
self.stateless_interactivity().drop_listeners.push((
TypeId::of::<W>(),
Box::new(move |view, dragged_view, cx| {
listener(view, dragged_view.downcast().unwrap(), cx);
@ -279,13 +304,13 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
}
pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
fn stateful_interaction(&mut self) -> &mut StatefulInteraction<V>;
fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V>;
fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
{
self.stateful_interaction().active_style = f(StyleRefinement::default());
self.stateful_interactivity().active_style = f(StyleRefinement::default());
self
}
@ -297,7 +322,7 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
where
Self: Sized,
{
self.stateful_interaction().group_active_style = Some(GroupStyle {
self.stateful_interactivity().group_active_style = Some(GroupStyle {
group: group_name.into(),
style: f(StyleRefinement::default()),
});
@ -311,7 +336,7 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
where
Self: Sized,
{
self.stateful_interaction()
self.stateful_interactivity()
.click_listeners
.push(Box::new(move |view, event, cx| listener(view, event, cx)));
self
@ -326,10 +351,10 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
W: 'static + Render,
{
debug_assert!(
self.stateful_interaction().drag_listener.is_none(),
self.stateful_interactivity().drag_listener.is_none(),
"calling on_drag more than once on the same element is not supported"
);
self.stateful_interaction().drag_listener =
self.stateful_interactivity().drag_listener =
Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
view: listener(view_state, cx).into(),
cursor_offset,
@ -342,10 +367,10 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
Self: Sized,
{
debug_assert!(
self.stateful_interaction().hover_listener.is_none(),
self.stateful_interactivity().hover_listener.is_none(),
"calling on_hover more than once on the same element is not supported"
);
self.stateful_interaction().hover_listener = Some(Box::new(listener));
self.stateful_interactivity().hover_listener = Some(Box::new(listener));
self
}
@ -358,10 +383,10 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
W: 'static + Render,
{
debug_assert!(
self.stateful_interaction().tooltip_builder.is_none(),
self.stateful_interactivity().tooltip_builder.is_none(),
"calling tooltip more than once on the same element is not supported"
);
self.stateful_interaction().tooltip_builder = Some(Arc::new(move |view_state, cx| {
self.stateful_interactivity().tooltip_builder = Some(Arc::new(move |view_state, cx| {
build_tooltip(view_state, cx).into()
}));
@ -369,11 +394,11 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
}
}
pub trait ElementInteraction<V: 'static>: 'static {
fn as_stateless(&self) -> &StatelessInteraction<V>;
fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V>;
fn as_stateful(&self) -> Option<&StatefulInteraction<V>>;
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>>;
pub trait ElementInteractivity<V: 'static>: 'static {
fn as_stateless(&self) -> &StatelessInteractivity<V>;
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V>;
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>>;
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>>;
fn initialize<R>(
&mut self,
@ -735,11 +760,11 @@ pub trait ElementInteraction<V: 'static>: 'static {
}
#[derive(Deref, DerefMut)]
pub struct StatefulInteraction<V> {
pub struct StatefulInteractivity<V> {
pub id: ElementId,
#[deref]
#[deref_mut]
stateless: StatelessInteraction<V>,
stateless: StatelessInteractivity<V>,
click_listeners: SmallVec<[ClickListener<V>; 2]>,
active_style: StyleRefinement,
group_active_style: Option<GroupStyle>,
@ -748,29 +773,29 @@ pub struct StatefulInteraction<V> {
tooltip_builder: Option<TooltipBuilder<V>>,
}
impl<V: 'static> ElementInteraction<V> for StatefulInteraction<V> {
fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
impl<V: 'static> ElementInteractivity<V> for StatefulInteractivity<V> {
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
Some(self)
}
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
Some(self)
}
fn as_stateless(&self) -> &StatelessInteraction<V> {
fn as_stateless(&self) -> &StatelessInteractivity<V> {
&self.stateless
}
fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
&mut self.stateless
}
}
impl<V> From<ElementId> for StatefulInteraction<V> {
impl<V> From<ElementId> for StatefulInteractivity<V> {
fn from(id: ElementId) -> Self {
Self {
id,
stateless: StatelessInteraction::default(),
stateless: StatelessInteractivity::default(),
click_listeners: SmallVec::new(),
drag_listener: None,
hover_listener: None,
@ -783,7 +808,7 @@ impl<V> From<ElementId> for StatefulInteraction<V> {
type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static;
pub struct StatelessInteraction<V> {
pub struct StatelessInteractivity<V> {
pub dispatch_context: DispatchContext,
pub mouse_down_listeners: SmallVec<[MouseDownListener<V>; 2]>,
pub mouse_up_listeners: SmallVec<[MouseUpListener<V>; 2]>,
@ -797,9 +822,9 @@ pub struct StatelessInteraction<V> {
drop_listeners: SmallVec<[(TypeId, Box<DropListener<V>>); 2]>,
}
impl<V> StatelessInteraction<V> {
pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteraction<V> {
StatefulInteraction {
impl<V> StatelessInteractivity<V> {
pub fn into_stateful(self, id: impl Into<ElementId>) -> StatefulInteractivity<V> {
StatefulInteractivity {
id: id.into(),
stateless: self,
click_listeners: SmallVec::new(),
@ -877,7 +902,7 @@ impl InteractiveElementState {
}
}
impl<V> Default for StatelessInteraction<V> {
impl<V> Default for StatelessInteractivity<V> {
fn default() -> Self {
Self {
dispatch_context: DispatchContext::default(),
@ -895,20 +920,20 @@ impl<V> Default for StatelessInteraction<V> {
}
}
impl<V: 'static> ElementInteraction<V> for StatelessInteraction<V> {
fn as_stateful(&self) -> Option<&StatefulInteraction<V>> {
impl<V: 'static> ElementInteractivity<V> for StatelessInteractivity<V> {
fn as_stateful(&self) -> Option<&StatefulInteractivity<V>> {
None
}
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteraction<V>> {
fn as_stateful_mut(&mut self) -> Option<&mut StatefulInteractivity<V>> {
None
}
fn as_stateless(&self) -> &StatelessInteraction<V> {
fn as_stateless(&self) -> &StatelessInteractivity<V> {
self
}
fn as_stateless_mut(&mut self) -> &mut StatelessInteraction<V> {
fn as_stateless_mut(&mut self) -> &mut StatelessInteractivity<V> {
self
}
}

View File

@ -277,7 +277,7 @@ impl Style {
pub fn paint<V: 'static>(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) {
let rem_size = cx.rem_size();
cx.stack(0, |cx| {
cx.with_z_index(0, |cx| {
cx.paint_shadows(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),
@ -287,7 +287,7 @@ impl Style {
let background_color = self.background.as_ref().and_then(Fill::color);
if background_color.is_some() || self.is_border_visible() {
cx.stack(1, |cx| {
cx.with_z_index(1, |cx| {
cx.paint_quad(
bounds,
self.corner_radii.to_pixels(bounds.size, rem_size),

View File

@ -142,8 +142,6 @@ impl Line {
color,
)?;
}
} else {
dbg!(content_mask.bounds, max_glyph_bounds);
}
}
}

View File

@ -1698,8 +1698,8 @@ impl<'a, V: 'static> ViewContext<'a, V> {
&mut self.window_cx
}
pub fn stack<R>(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R {
self.window.z_index_stack.push(order);
pub fn with_z_index<R>(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R {
self.window.z_index_stack.push(z_index);
let result = f(self);
self.window.z_index_stack.pop();
result

View File

@ -9,4 +9,5 @@ path = "src/menu2.rs"
doctest = false
[dependencies]
gpui = { package = "gpui2", path = "../gpui2" }
serde.workspace = true
serde_derive.workspace = true

View File

@ -1,25 +1,25 @@
// todo!(use actions! macro)
use serde_derive::Deserialize;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct Cancel;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct Confirm;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SecondaryConfirm;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SelectPrev;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SelectNext;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SelectFirst;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct SelectLast;
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
pub struct ShowContextMenu;

View File

@ -18,11 +18,11 @@
// Dismiss,
// }
use std::ops::Range;
use std::cmp;
use gpui::{
div, list, red, AppContext, Component, Div, Element, ElementId, ParentElement, Render, Styled,
ViewContext,
div, list, Component, ElementId, FocusHandle, Focusable, ParentElement, StatelessInteractive,
Styled, ViewContext,
};
// pub struct Picker<D> {
@ -43,8 +43,9 @@ pub trait PickerDelegate: Sized + 'static {
fn match_count(&self, picker_id: ElementId) -> usize;
// fn selected_index(&self) -> usize;
// fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
fn selected_index(&self, picker_id: ElementId) -> usize;
fn set_selected_index(&mut self, ix: usize, picker_id: ElementId, cx: &mut ViewContext<Self>);
// fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
// fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
// fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
@ -53,8 +54,6 @@ pub trait PickerDelegate: Sized + 'static {
fn render_match(
&self,
ix: usize,
active: bool,
hovered: bool,
selected: bool,
picker_id: ElementId,
cx: &mut ViewContext<Self>,
@ -84,32 +83,72 @@ pub trait PickerDelegate: Sized + 'static {
#[derive(Component)]
pub struct Picker<V: PickerDelegate> {
id: ElementId,
focus_handle: FocusHandle,
phantom: std::marker::PhantomData<V>,
}
impl<V: PickerDelegate> Picker<V> {
pub fn new(id: impl Into<ElementId>) -> Self {
pub fn new(id: impl Into<ElementId>, focus_handle: FocusHandle) -> Self {
Self {
id: id.into(),
focus_handle,
phantom: std::marker::PhantomData,
}
}
}
impl<V: 'static + PickerDelegate> Picker<V> {
pub fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
div().size_full().id(self.id.clone()).child(
list(
"candidates",
view.match_count(self.id.clone()),
move |this: &mut V, visible_range, cx| {
visible_range
.map(|ix| this.render_match(ix, false, false, false, self.id.clone(), cx))
.collect()
},
pub fn render(self, view: &mut V, _cx: &mut ViewContext<V>) -> impl Component<V> {
let id = self.id.clone();
div()
.size_full()
.id(self.id.clone())
.track_focus(&self.focus_handle)
.context("picker")
.on_focus(|v, e, cx| {
dbg!("FOCUSED!");
})
.on_blur(|v, e, cx| {
dbg!("BLURRED!");
})
.on_action({
let id = id.clone();
move |view: &mut V, _: &menu::SelectNext, cx| {
let index = view.selected_index(id.clone());
let count = view.match_count(id.clone());
if count > 0 {
view.set_selected_index(cmp::min(index + 1, count - 1), id.clone(), cx);
}
}
})
.on_action({
let id = id.clone();
move |view, _: &menu::SelectPrev, cx| {
let index = view.selected_index(id.clone());
let count = view.match_count(id.clone());
if count > 0 {
view.set_selected_index((index + 1) % count, id.clone(), cx);
}
}
})
.on_action(|view, _: &menu::SelectFirst, cx| {})
.on_action(|view, _: &menu::SelectLast, cx| {})
.on_action(|view, _: &menu::Cancel, cx| {})
.on_action(|view, _: &menu::Confirm, cx| {})
.on_action(|view, _: &menu::SecondaryConfirm, cx| {})
.child(
list(
"candidates",
view.match_count(self.id.clone()),
move |view: &mut V, visible_range, cx| {
let selected_ix = view.selected_index(self.id.clone());
visible_range
.map(|ix| view.render_match(ix, ix == selected_ix, self.id.clone(), cx))
.collect()
},
)
.size_full(),
)
.size_full(),
)
}
}

View File

@ -25,6 +25,7 @@ smallvec.workspace = true
strum = { version = "0.25.0", features = ["derive"] }
theme = { path = "../theme" }
theme2 = { path = "../theme2" }
menu = { package = "menu2", path = "../menu2" }
ui = { package = "ui2", path = "../ui2", features = ["stories"] }
util = { path = "../util" }
picker = { package = "picker2", path = "../picker2" }

View File

@ -1,5 +1,5 @@
use gpui::{
div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render, StatefulInteraction,
div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render, StatefulInteractivity,
StatelessInteractive, Styled, View, VisualContext, WindowContext,
};
use serde::Deserialize;
@ -31,7 +31,7 @@ impl FocusStory {
}
impl Render for FocusStory {
type Element = Div<Self, StatefulInteraction<Self>, FocusEnabled<Self>>;
type Element = Div<Self, StatefulInteractivity<Self>, FocusEnabled<Self>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
@ -48,20 +48,18 @@ impl Render for FocusStory {
.id("parent")
.focusable()
.context("parent")
.on_action(|_, action: &ActionA, phase, cx| {
println!("Action A dispatched on parent during {:?}", phase);
.on_action(|_, action: &ActionA, cx| {
println!("Action A dispatched on parent during");
})
.on_action(|_, action: &ActionB, phase, cx| {
println!("Action B dispatched on parent during {:?}", phase);
.on_action(|_, action: &ActionB, cx| {
println!("Action B dispatched on parent during");
})
.on_focus(|_, _, _| println!("Parent focused"))
.on_blur(|_, _, _| println!("Parent blurred"))
.on_focus_in(|_, _, _| println!("Parent focus_in"))
.on_focus_out(|_, _, _| println!("Parent focus_out"))
.on_key_down(|_, event, phase, _| {
println!("Key down on parent {:?} {:?}", phase, event)
})
.on_key_up(|_, event, phase, _| println!("Key up on parent {:?} {:?}", phase, event))
.on_key_down(|_, event, phase, _| println!("Key down on parent {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on parent {:?}", event))
.size_full()
.bg(color_1)
.focus(|style| style.bg(color_2))
@ -70,8 +68,8 @@ impl Render for FocusStory {
div()
.track_focus(&child_1)
.context("child-1")
.on_action(|_, action: &ActionB, phase, cx| {
println!("Action B dispatched on child 1 during {:?}", phase);
.on_action(|_, action: &ActionB, cx| {
println!("Action B dispatched on child 1 during");
})
.w_full()
.h_6()
@ -82,20 +80,16 @@ impl Render for FocusStory {
.on_blur(|_, _, _| println!("Child 1 blurred"))
.on_focus_in(|_, _, _| println!("Child 1 focus_in"))
.on_focus_out(|_, _, _| println!("Child 1 focus_out"))
.on_key_down(|_, event, phase, _| {
println!("Key down on child 1 {:?} {:?}", phase, event)
})
.on_key_up(|_, event, phase, _| {
println!("Key up on child 1 {:?} {:?}", phase, event)
})
.on_key_down(|_, event, phase, _| println!("Key down on child 1 {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on child 1 {:?}", event))
.child("Child 1"),
)
.child(
div()
.track_focus(&child_2)
.context("child-2")
.on_action(|_, action: &ActionC, phase, cx| {
println!("Action C dispatched on child 2 during {:?}", phase);
.on_action(|_, action: &ActionC, cx| {
println!("Action C dispatched on child 2 during");
})
.w_full()
.h_6()
@ -104,12 +98,8 @@ impl Render for FocusStory {
.on_blur(|_, _, _| println!("Child 2 blurred"))
.on_focus_in(|_, _, _| println!("Child 2 focus_in"))
.on_focus_out(|_, _, _| println!("Child 2 focus_out"))
.on_key_down(|_, event, phase, _| {
println!("Key down on child 2 {:?} {:?}", phase, event)
})
.on_key_up(|_, event, phase, _| {
println!("Key up on child 2 {:?} {:?}", phase, event)
})
.on_key_down(|_, event, phase, _| println!("Key down on child 2 {:?}", event))
.on_key_up(|_, event, phase, _| println!("Key up on child 2 {:?}", event))
.child("Child 2"),
)
}

View File

@ -1,5 +1,5 @@
use crate::{story::Story, story_selector::ComponentStory};
use gpui::{Div, Render, StatefulInteraction, View, VisualContext};
use gpui::{Div, Render, StatefulInteractivity, View, VisualContext};
use strum::IntoEnumIterator;
use ui::prelude::*;
@ -12,7 +12,7 @@ impl KitchenSinkStory {
}
impl Render for KitchenSinkStory {
type Element = Div<Self, StatefulInteraction<Self>>;
type Element = Div<Self, StatefulInteractivity<Self>>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let component_stories = ComponentStory::iter()

View File

@ -1,15 +1,18 @@
use gpui::{
black, div, red, Div, Fill, ParentElement, Render, SharedString, Styled, View, VisualContext,
WindowContext,
div, Component, Div, FocusHandle, KeyBinding, ParentElement, Render, SharedString,
StatelessInteractive, Styled, View, VisualContext, WindowContext,
};
use picker::{Picker, PickerDelegate};
use theme2::ActiveTheme;
pub struct PickerStory {
selected_ix: usize,
candidates: Vec<SharedString>,
focus_handle: FocusHandle,
}
impl PickerDelegate for PickerStory {
type ListItem = SharedString;
type ListItem = Div<Self>;
fn match_count(&self, _picker_id: gpui::ElementId) -> usize {
self.candidates.len()
@ -18,46 +21,118 @@ impl PickerDelegate for PickerStory {
fn render_match(
&self,
ix: usize,
_active: bool,
_hovered: bool,
_selected: bool,
selected: bool,
_picker_id: gpui::ElementId,
cx: &mut gpui::ViewContext<Self>,
) -> Self::ListItem {
self.candidates[ix].clone()
let colors = cx.theme().colors();
div()
.text_color(colors.text)
.when(selected, |s| {
s.border_l_10().border_color(colors.terminal_ansi_yellow)
})
.hover(|style| {
style
.bg(colors.element_active)
.text_color(colors.text_accent)
})
.child(self.candidates[ix].clone())
}
fn selected_index(&self, picker_id: gpui::ElementId) -> usize {
self.selected_ix
}
fn set_selected_index(
&mut self,
ix: usize,
_picker_id: gpui::ElementId,
_cx: &mut gpui::ViewContext<Self>,
) {
self.selected_ix = ix;
}
}
impl PickerStory {
pub fn new(cx: &mut WindowContext) -> View<Self> {
cx.build_view(|cx| PickerStory {
candidates: vec![
"Pizza (Italy)".into(),
"Sushi (Japan)".into(),
"Paella (Spain)".into(),
"Tacos (Mexico)".into(),
"Peking Duck (China)".into(),
"Fish and Chips (UK)".into(),
"Croissant (France)".into(),
"Bratwurst (Germany)".into(),
"Poutine (Canada)".into(),
"Chicken Tikka Masala (India)".into(),
"Feijoada (Brazil)".into(),
"Kimchi (Korea)".into(),
"Borscht (Ukraine)".into(),
"Falafel (Middle East)".into(),
"Baklava (Turkey)".into(),
"Shepherd's Pie (Ireland)".into(),
"Rendang (Indonesia)".into(),
"Kebab (Middle East)".into(),
"Ceviche (Peru)".into(),
"Pierogi (Poland)".into(),
"Churrasco (Brazil)".into(),
"Moussaka (Greece)".into(),
"Lasagna (Italy)".into(),
"Pad Thai (Thailand)".into(),
"Pho (Vietnam)".into(),
],
cx.build_view(|cx| {
cx.bind_keys([
KeyBinding::new("up", menu::SelectPrev, Some("picker")),
KeyBinding::new("pageup", menu::SelectFirst, Some("picker")),
KeyBinding::new("shift-pageup", menu::SelectFirst, Some("picker")),
KeyBinding::new("ctrl-p", menu::SelectPrev, Some("picker")),
KeyBinding::new("down", menu::SelectNext, Some("picker")),
KeyBinding::new("pagedown", menu::SelectLast, Some("picker")),
KeyBinding::new("shift-pagedown", menu::SelectFirst, Some("picker")),
KeyBinding::new("ctrl-n", menu::SelectNext, Some("picker")),
KeyBinding::new("cmd-up", menu::SelectFirst, Some("picker")),
KeyBinding::new("cmd-down", menu::SelectLast, Some("picker")),
KeyBinding::new("enter", menu::Confirm, Some("picker")),
KeyBinding::new("ctrl-enter", menu::ShowContextMenu, Some("picker")),
KeyBinding::new("cmd-enter", menu::SecondaryConfirm, Some("picker")),
KeyBinding::new("escape", menu::Cancel, Some("picker")),
KeyBinding::new("ctrl-c", menu::Cancel, Some("picker")),
]);
let fh = cx.focus_handle();
cx.focus(&fh);
PickerStory {
focus_handle: fh,
candidates: vec![
"Baguette (France)".into(),
"Baklava (Turkey)".into(),
"Beef Wellington (UK)".into(),
"Biryani (India)".into(),
"Borscht (Ukraine)".into(),
"Bratwurst (Germany)".into(),
"Bulgogi (Korea)".into(),
"Burrito (USA)".into(),
"Ceviche (Peru)".into(),
"Chicken Tikka Masala (India)".into(),
"Churrasco (Brazil)".into(),
"Couscous (North Africa)".into(),
"Croissant (France)".into(),
"Dim Sum (China)".into(),
"Empanada (Argentina)".into(),
"Fajitas (Mexico)".into(),
"Falafel (Middle East)".into(),
"Feijoada (Brazil)".into(),
"Fish and Chips (UK)".into(),
"Fondue (Switzerland)".into(),
"Goulash (Hungary)".into(),
"Haggis (Scotland)".into(),
"Kebab (Middle East)".into(),
"Kimchi (Korea)".into(),
"Lasagna (Italy)".into(),
"Maple Syrup Pancakes (Canada)".into(),
"Moussaka (Greece)".into(),
"Pad Thai (Thailand)".into(),
"Paella (Spain)".into(),
"Pancakes (USA)".into(),
"Pasta Carbonara (Italy)".into(),
"Pavlova (Australia)".into(),
"Peking Duck (China)".into(),
"Pho (Vietnam)".into(),
"Pierogi (Poland)".into(),
"Pizza (Italy)".into(),
"Poutine (Canada)".into(),
"Pretzel (Germany)".into(),
"Ramen (Japan)".into(),
"Rendang (Indonesia)".into(),
"Sashimi (Japan)".into(),
"Satay (Indonesia)".into(),
"Shepherd's Pie (Ireland)".into(),
"Sushi (Japan)".into(),
"Tacos (Mexico)".into(),
"Tandoori Chicken (India)".into(),
"Tortilla (Spain)".into(),
"Tzatziki (Greece)".into(),
"Wiener Schnitzel (Austria)".into(),
],
selected_ix: 0,
}
})
}
}
@ -66,9 +141,11 @@ impl Render for PickerStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();
div()
.text_color(red())
.bg(theme.styles.colors.background)
.size_full()
.child(Picker::new("picker_story"))
.child(Picker::new("picker_story", self.focus_handle.clone()))
}
}

View File

@ -1,5 +1,5 @@
use gpui::{
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteraction, Styled,
div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteractivity, Styled,
View, VisualContext, WindowContext,
};
use theme2::ActiveTheme;
@ -13,7 +13,7 @@ impl ScrollStory {
}
impl Render for ScrollStory {
type Element = Div<Self, StatefulInteraction<Self>>;
type Element = Div<Self, StatefulInteractivity<Self>>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
let theme = cx.theme();

View File

@ -128,7 +128,7 @@ impl<V: 'static> Checkbox<V> {
// click area for the checkbox.
.size_5()
// Because we've enlarged the click area, we need to create a
// `group` to pass down interaction events to the checkbox.
// `group` to pass down interactivity events to the checkbox.
.group(group_id.clone())
.child(
div()
@ -148,7 +148,7 @@ impl<V: 'static> Checkbox<V> {
.bg(bg_color)
.border()
.border_color(border_color)
// We only want the interaction states to fire when we
// We only want the interactivity states to fire when we
// are in a checkbox that isn't disabled.
.when(!self.disabled, |this| {
// Here instead of `hover()` we use `group_hover()`

View File

@ -37,4 +37,3 @@
((symbol) @comment
(#match? @comment "^#[cC][iIsS]$"))

View File

@ -37,4 +37,3 @@
((symbol) @comment
(#match? @comment "^#[cC][iIsS]$"))