Detailed changes
@@ -4987,7 +4987,8 @@ dependencies = [
name = "menu2"
version = "0.1.0"
dependencies = [
- "gpui2",
+ "serde",
+ "serde_derive",
]
[[package]]
@@ -6023,6 +6024,23 @@ dependencies = [
"workspace",
]
+[[package]]
+name = "picker2"
+version = "0.1.0"
+dependencies = [
+ "ctor",
+ "editor2",
+ "env_logger 0.9.3",
+ "gpui2",
+ "menu2",
+ "parking_lot 0.11.2",
+ "serde_json",
+ "settings2",
+ "theme2",
+ "util",
+ "workspace2",
+]
+
[[package]]
name = "pico-args"
version = "0.4.2"
@@ -8539,9 +8557,14 @@ dependencies = [
"backtrace-on-stack-overflow",
"chrono",
"clap 4.4.4",
+ "editor2",
+ "fuzzy2",
"gpui2",
"itertools 0.11.0",
+ "language2",
"log",
+ "menu2",
+ "picker2",
"rust-embed",
"serde",
"settings2",
@@ -68,6 +68,7 @@ members = [
"crates/notifications",
"crates/outline",
"crates/picker",
+ "crates/picker2",
"crates/plugin",
"crates/plugin_macros",
"crates/plugin_runtime",
@@ -9192,7 +9192,7 @@ impl Editor {
supports
}
- fn focus(&self, cx: &mut WindowContext) {
+ pub fn focus(&self, cx: &mut WindowContext) {
cx.focus(&self.focus_handle)
}
}
@@ -19,8 +19,8 @@ use gpui::{
BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, DispatchContext, DispatchPhase,
Edges, Element, ElementId, Entity, GlobalElementId, Hsla, KeyDownEvent, KeyListener, KeyMatch,
Line, Modifiers, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
- ScrollWheelEvent, ShapedGlyph, Size, StatefulInteraction, Style, TextRun, TextStyle,
- TextSystem, ViewContext, WindowContext,
+ ScrollWheelEvent, ShapedGlyph, Size, Style, TextRun, TextStyle, TextSystem, ViewContext,
+ WindowContext,
};
use itertools::Itertools;
use language::language_settings::ShowWhitespaceSetting;
@@ -3220,7 +3220,7 @@ impl PositionMap {
let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right);
- let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance).into();
+ let column_overshoot_after_line_end = (x_overshoot_after_line_end / self.em_advance) as u32;
*exact_unclipped.column_mut() += column_overshoot_after_line_end;
PointForPosition {
previous_valid,
@@ -13,6 +13,7 @@ use std::{
atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Weak,
},
+ thread::panicking,
};
slotmap::new_key_type! { pub struct EntityId; }
@@ -140,9 +141,8 @@ impl<'a, T: 'static> core::ops::DerefMut for Lease<'a, T> {
impl<'a, T> Drop for Lease<'a, T> {
fn drop(&mut self) {
- if self.entity.is_some() {
- // We don't panic here, because other panics can cause us to drop the lease without ending it cleanly.
- log::error!("Leases must be ended with EntityMap::end_lease")
+ if self.entity.is_some() && !panicking() {
+ panic!("Leases must be ended with EntityMap::end_lease")
}
}
}
@@ -203,6 +203,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 {
@@ -2,8 +2,10 @@ mod div;
mod img;
mod svg;
mod text;
+mod uniform_list;
pub use div::*;
pub use img::*;
pub use svg::*;
pub use text::*;
+pub use uniform_list::*;
@@ -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())
}
@@ -209,7 +213,7 @@ where
cx: &mut ViewContext<V>,
) -> Self::ElementState {
let mut element_state = element_state.unwrap_or_default();
- self.interaction.initialize(cx, |cx| {
+ self.interactivity.initialize(cx, |cx| {
self.focus
.initialize(element_state.focus_handle.take(), cx, |focus_handle, cx| {
element_state.focus_handle = focus_handle;
@@ -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
}
}
@@ -1,15 +1,15 @@
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>,
@@ -17,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,
@@ -28,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 {
@@ -42,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,
@@ -57,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> {
@@ -67,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;
@@ -101,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);
});
@@ -118,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()
});
@@ -136,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 {
@@ -146,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()
@@ -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()
@@ -127,6 +127,7 @@ impl<V: 'static> Element<V> for Text<V> {
let element_state = element_state
.as_ref()
.expect("measurement has not been performed");
+
let line_height = element_state.line_height;
let mut line_origin = bounds.origin;
for line in &element_state.lines {
@@ -0,0 +1,244 @@
+use crate::{
+ point, px, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
+ ElementInteractivity, InteractiveElementState, LayoutId, Pixels, Point, Size,
+ StatefulInteractive, StatefulInteractivity, StatelessInteractive, StatelessInteractivity,
+ StyleRefinement, Styled, ViewContext,
+};
+use parking_lot::Mutex;
+use smallvec::SmallVec;
+use std::{cmp, ops::Range, sync::Arc};
+use taffy::style::Overflow;
+
+pub fn uniform_list<Id, V, C>(
+ id: Id,
+ item_count: usize,
+ f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[C; 64]>,
+) -> UniformList<V>
+where
+ Id: Into<ElementId>,
+ V: 'static,
+ C: Component<V>,
+{
+ let id = id.into();
+ UniformList {
+ id: id.clone(),
+ style: Default::default(),
+ item_count,
+ render_items: Box::new(move |view, visible_range, cx| {
+ f(view, visible_range, cx)
+ .into_iter()
+ .map(|component| component.render())
+ .collect()
+ }),
+ interactivity: id.into(),
+ scroll_handle: None,
+ }
+}
+
+pub struct UniformList<V: 'static> {
+ id: ElementId,
+ style: StyleRefinement,
+ item_count: usize,
+ render_items: Box<
+ dyn for<'a> Fn(
+ &'a mut V,
+ Range<usize>,
+ &'a mut ViewContext<V>,
+ ) -> SmallVec<[AnyElement<V>; 64]>,
+ >,
+ interactivity: StatefulInteractivity<V>,
+ scroll_handle: Option<UniformListScrollHandle>,
+}
+
+#[derive(Clone)]
+pub struct UniformListScrollHandle(Arc<Mutex<Option<ScrollHandleState>>>);
+
+#[derive(Clone, Debug)]
+struct ScrollHandleState {
+ item_height: Pixels,
+ list_height: Pixels,
+ scroll_offset: Arc<Mutex<Point<Pixels>>>,
+}
+
+impl UniformListScrollHandle {
+ pub fn new() -> Self {
+ Self(Arc::new(Mutex::new(None)))
+ }
+
+ pub fn scroll_to_item(&self, ix: usize) {
+ if let Some(state) = &*self.0.lock() {
+ let mut scroll_offset = state.scroll_offset.lock();
+ let item_top = state.item_height * ix;
+ let item_bottom = item_top + state.item_height;
+ let scroll_top = -scroll_offset.y;
+ if item_top < scroll_top {
+ scroll_offset.y = -item_top;
+ } else if item_bottom > scroll_top + state.list_height {
+ scroll_offset.y = -(item_bottom - state.list_height);
+ }
+ }
+ }
+}
+
+impl<V: 'static> Styled for UniformList<V> {
+ fn style(&mut self) -> &mut StyleRefinement {
+ &mut self.style
+ }
+}
+
+impl<V: 'static> Element<V> for UniformList<V> {
+ type ElementState = InteractiveElementState;
+
+ fn id(&self) -> Option<crate::ElementId> {
+ Some(self.id.clone())
+ }
+
+ fn initialize(
+ &mut self,
+ _: &mut V,
+ element_state: Option<Self::ElementState>,
+ _: &mut ViewContext<V>,
+ ) -> Self::ElementState {
+ element_state.unwrap_or_default()
+ }
+
+ fn layout(
+ &mut self,
+ _view_state: &mut V,
+ _element_state: &mut Self::ElementState,
+ cx: &mut ViewContext<V>,
+ ) -> LayoutId {
+ cx.request_layout(&self.computed_style(), None)
+ }
+
+ fn paint(
+ &mut self,
+ bounds: crate::Bounds<crate::Pixels>,
+ view_state: &mut V,
+ element_state: &mut Self::ElementState,
+ cx: &mut ViewContext<V>,
+ ) {
+ let style = self.computed_style();
+ style.paint(bounds, cx);
+
+ let border = style.border_widths.to_pixels(cx.rem_size());
+ let padding = style.padding.to_pixels(bounds.size.into(), cx.rem_size());
+
+ let padded_bounds = Bounds::from_corners(
+ bounds.origin + point(border.left + padding.left, border.top + padding.top),
+ bounds.lower_right()
+ - point(border.right + padding.right, border.bottom + padding.bottom),
+ );
+
+ 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);
+ if let Some(scroll_handle) = self.scroll_handle.clone() {
+ scroll_handle.0.lock().replace(ScrollHandleState {
+ item_height,
+ list_height: padded_bounds.size.height,
+ scroll_offset: element_state.track_scroll_offset(),
+ });
+ }
+ let visible_item_count = if item_height > px(0.) {
+ (padded_bounds.size.height / item_height).ceil() as usize + 1
+ } else {
+ 0
+ };
+ let scroll_offset = element_state
+ .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.clone(), cx);
+
+ content_size = Size {
+ width: padded_bounds.size.width,
+ height: item_height * self.item_count,
+ };
+
+ 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 + 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, element_state, cx);
+ });
+ })
+ }
+}
+
+impl<V> UniformList<V> {
+ fn measure_item_height(
+ &self,
+ view_state: &mut V,
+ list_bounds: Bounds<Pixels>,
+ cx: &mut ViewContext<V>,
+ ) -> Pixels {
+ let mut items = (self.render_items)(view_state, 0..1, cx);
+ debug_assert!(items.len() == 1);
+ let mut item_to_measure = items.pop().unwrap();
+ item_to_measure.initialize(view_state, cx);
+ let layout_id = item_to_measure.layout(view_state, cx);
+ cx.compute_layout(
+ layout_id,
+ Size {
+ width: AvailableSpace::Definite(list_bounds.size.width),
+ height: AvailableSpace::MinContent,
+ },
+ );
+ cx.layout_bounds(layout_id).size.height
+ }
+
+ pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
+ self.scroll_handle = Some(handle);
+ self
+ }
+}
+
+impl<V: 'static> StatelessInteractive<V> for UniformList<V> {
+ fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<V> {
+ self.interactivity.as_stateless_mut()
+ }
+}
+
+impl<V: 'static> StatefulInteractive<V> for UniformList<V> {
+ fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<V> {
+ &mut self.interactivity
+ }
+}
+
+impl<V: 'static> Component<V> for UniformList<V> {
+ fn render(self) -> AnyElement<V> {
+ AnyElement::new(self)
+ }
+}
@@ -267,6 +267,24 @@ impl From<Size<Pixels>> for Size<GlobalPixels> {
}
}
+impl From<Size<Pixels>> for Size<DefiniteLength> {
+ fn from(size: Size<Pixels>) -> Self {
+ Size {
+ width: size.width.into(),
+ height: size.height.into(),
+ }
+ }
+}
+
+impl From<Size<Pixels>> for Size<AbsoluteLength> {
+ fn from(size: Size<Pixels>) -> Self {
+ Size {
+ width: size.width.into(),
+ height: size.height.into(),
+ }
+ }
+}
+
impl Size<Length> {
pub fn full() -> Self {
Self {
@@ -558,6 +576,15 @@ impl Edges<DefiniteLength> {
left: px(0.).into(),
}
}
+
+ pub fn to_pixels(&self, parent_size: Size<AbsoluteLength>, rem_size: Pixels) -> Edges<Pixels> {
+ Edges {
+ top: self.top.to_pixels(parent_size.height, rem_size),
+ right: self.right.to_pixels(parent_size.width, rem_size),
+ bottom: self.bottom.to_pixels(parent_size.height, rem_size),
+ left: self.left.to_pixels(parent_size.width, rem_size),
+ }
+ }
}
impl Edges<AbsoluteLength> {
@@ -689,16 +716,16 @@ impl<T> Copy for Corners<T> where T: Copy + Clone + Default + Debug {}
pub struct Pixels(pub(crate) f32);
impl std::ops::Div for Pixels {
- type Output = Self;
+ type Output = f32;
fn div(self, rhs: Self) -> Self::Output {
- Self(self.0 / rhs.0)
+ self.0 / rhs.0
}
}
impl std::ops::DivAssign for Pixels {
fn div_assign(&mut self, rhs: Self) {
- self.0 /= rhs.0;
+ *self = Self(self.0 / rhs.0);
}
}
@@ -750,14 +777,6 @@ impl Pixels {
pub const ZERO: Pixels = Pixels(0.0);
pub const MAX: Pixels = Pixels(f32::MAX);
- pub fn as_usize(&self) -> usize {
- self.0 as usize
- }
-
- pub fn as_isize(&self) -> isize {
- self.0 as isize
- }
-
pub fn floor(&self) -> Self {
Self(self.0.floor())
}
@@ -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
}
+ /// Capture the given action, fires during the capture phase
+ fn capture_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::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, 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::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,
@@ -736,11 +761,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>,
@@ -749,29 +774,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,
@@ -784,7 +809,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]>,
@@ -798,9 +823,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(),
@@ -876,9 +901,15 @@ impl InteractiveElementState {
.as_ref()
.map(|offset| offset.lock().clone())
}
+
+ pub fn track_scroll_offset(&mut self) -> Arc<Mutex<Point<Pixels>>> {
+ self.scroll_offset
+ .get_or_insert_with(|| Arc::new(Mutex::new(Default::default())))
+ .clone()
+ }
}
-impl<V> Default for StatelessInteraction<V> {
+impl<V> Default for StatelessInteractivity<V> {
fn default() -> Self {
Self {
dispatch_context: DispatchContext::default(),
@@ -896,20 +927,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
}
}
@@ -1236,7 +1267,7 @@ pub type KeyListener<V> = Box<
mod test {
use crate::{
self as gpui, div, Div, FocusHandle, KeyBinding, Keystroke, ParentElement, Render,
- StatefulInteraction, StatelessInteractive, TestAppContext, VisualContext,
+ StatefulInteractivity, StatelessInteractive, TestAppContext, VisualContext,
};
struct TestView {
@@ -1248,7 +1279,7 @@ mod test {
actions!(TestAction);
impl Render for TestView {
- type Element = Div<Self, StatefulInteraction<Self>>;
+ type Element = Div<Self, StatefulInteractivity<Self>>;
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
div().id("testview").child(
@@ -1257,7 +1288,7 @@ mod test {
dbg!("ola!");
this.saw_key_down = true
})
- .on_action(|this: &mut TestView, _: &TestAction, _, _| {
+ .on_action(|this: &mut TestView, _: &TestAction, _| {
dbg!("ola!");
this.saw_action = true
})
@@ -281,7 +281,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),
@@ -291,7 +291,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),
@@ -1,14 +1,19 @@
use crate::{
self as gpui, hsla, point, px, relative, rems, AbsoluteLength, AlignItems, CursorStyle,
DefiniteLength, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position,
- SharedString, StyleRefinement, Visibility,
+ SharedString, Style, StyleRefinement, Visibility,
};
use crate::{BoxShadow, TextStyleRefinement};
+use refineable::Refineable;
use smallvec::smallvec;
pub trait Styled {
fn style(&mut self) -> &mut StyleRefinement;
+ fn computed_style(&mut self) -> Style {
+ Style::default().refined(self.style().clone())
+ }
+
gpui2_macros::style_helpers!();
/// Sets the size of the element to the full width and height.
@@ -74,7 +74,6 @@ impl Line {
glyph_origin.y += line_height;
}
prev_glyph_position = glyph.position;
- let glyph_origin = glyph_origin + baseline_offset;
let mut finished_underline: Option<(Point<Pixels>, UnderlineStyle)> = None;
if glyph.index >= run_end {
@@ -125,14 +124,14 @@ impl Line {
if max_glyph_bounds.intersects(&content_mask.bounds) {
if glyph.is_emoji {
cx.paint_emoji(
- glyph_origin,
+ glyph_origin + baseline_offset,
run.font_id,
glyph.id,
self.layout.layout.font_size,
)?;
} else {
cx.paint_glyph(
- glyph_origin,
+ glyph_origin + baseline_offset,
run.font_id,
glyph.id,
self.layout.layout.font_size,
@@ -563,6 +563,12 @@ impl<'a> WindowContext<'a> {
.request_measured_layout(style, rem_size, measure)
}
+ pub fn compute_layout(&mut self, layout_id: LayoutId, available_space: Size<AvailableSpace>) {
+ self.window
+ .layout_engine
+ .compute_layout(layout_id, available_space)
+ }
+
/// Obtain the bounds computed for the given LayoutId relative to the window. This method should not
/// be invoked until the paint phase begins, and will usually be invoked by GPUI itself automatically
/// in order to pass your element its `Bounds` automatically.
@@ -794,6 +800,7 @@ impl<'a> WindowContext<'a> {
}
/// Paint a monochrome (non-emoji) glyph into the scene for the current frame at the current z-index.
+ /// The y component of the origin is the baseline of the glyph.
pub fn paint_glyph(
&mut self,
origin: Point<Pixels>,
@@ -847,6 +854,7 @@ impl<'a> WindowContext<'a> {
}
/// Paint an emoji glyph into the scene for the current frame at the current z-index.
+ /// The y component of the origin is the baseline of the glyph.
pub fn paint_emoji(
&mut self,
origin: Point<Pixels>,
@@ -1707,8 +1715,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
@@ -9,4 +9,5 @@ path = "src/menu2.rs"
doctest = false
[dependencies]
-gpui = { package = "gpui2", path = "../gpui2" }
+serde.workspace = true
+serde_derive.workspace = true
@@ -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;
@@ -0,0 +1,28 @@
+[package]
+name = "picker2"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+path = "src/picker2.rs"
+doctest = false
+
+[dependencies]
+editor = { package = "editor2", path = "../editor2" }
+gpui = { package = "gpui2", path = "../gpui2" }
+menu = { package = "menu2", path = "../menu2" }
+settings = { package = "settings2", path = "../settings2" }
+util = { path = "../util" }
+theme = { package = "theme2", path = "../theme2" }
+workspace = { package = "workspace2", path = "../workspace2" }
+
+parking_lot.workspace = true
+
+[dev-dependencies]
+editor = { package = "editor2", path = "../editor2", features = ["test-support"] }
+gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
+serde_json.workspace = true
+workspace = { package = "workspace2", path = "../workspace2", features = ["test-support"] }
+ctor.workspace = true
+env_logger.workspace = true
@@ -0,0 +1,163 @@
+use editor::Editor;
+use gpui::{
+ div, uniform_list, Component, Div, FocusEnabled, ParentElement, Render, StatefulInteractivity,
+ StatelessInteractive, Styled, Task, UniformListScrollHandle, View, ViewContext, VisualContext,
+ WindowContext,
+};
+use std::cmp;
+
+pub struct Picker<D: PickerDelegate> {
+ pub delegate: D,
+ scroll_handle: UniformListScrollHandle,
+ editor: View<Editor>,
+ pending_update_matches: Option<Task<Option<()>>>,
+}
+
+pub trait PickerDelegate: Sized + 'static {
+ type ListItem: Component<Picker<Self>>;
+
+ fn match_count(&self) -> usize;
+ fn selected_index(&self) -> usize;
+ fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
+
+ // fn placeholder_text(&self) -> Arc<str>;
+ 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>>);
+
+ fn render_match(
+ &self,
+ ix: usize,
+ selected: bool,
+ cx: &mut ViewContext<Picker<Self>>,
+ ) -> Self::ListItem;
+}
+
+impl<D: PickerDelegate> Picker<D> {
+ pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
+ let editor = cx.build_view(|cx| Editor::single_line(cx));
+ cx.subscribe(&editor, Self::on_input_editor_event).detach();
+ Self {
+ delegate,
+ scroll_handle: UniformListScrollHandle::new(),
+ pending_update_matches: None,
+ editor,
+ }
+ }
+
+ pub fn focus(&self, cx: &mut WindowContext) {
+ self.editor.update(cx, |editor, cx| editor.focus(cx));
+ }
+
+ fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
+ let count = self.delegate.match_count();
+ if count > 0 {
+ let index = self.delegate.selected_index();
+ let ix = cmp::min(index + 1, count - 1);
+ self.delegate.set_selected_index(ix, cx);
+ self.scroll_handle.scroll_to_item(ix);
+ }
+ }
+
+ fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
+ let count = self.delegate.match_count();
+ if count > 0 {
+ let index = self.delegate.selected_index();
+ let ix = index.saturating_sub(1);
+ self.delegate.set_selected_index(ix, cx);
+ self.scroll_handle.scroll_to_item(ix);
+ }
+ }
+
+ fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext<Self>) {
+ let count = self.delegate.match_count();
+ if count > 0 {
+ self.delegate.set_selected_index(0, cx);
+ self.scroll_handle.scroll_to_item(0);
+ }
+ }
+
+ fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext<Self>) {
+ let count = self.delegate.match_count();
+ if count > 0 {
+ self.delegate.set_selected_index(count - 1, cx);
+ self.scroll_handle.scroll_to_item(count - 1);
+ }
+ }
+
+ fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
+ self.delegate.dismissed(cx);
+ }
+
+ fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
+ self.delegate.confirm(false, cx);
+ }
+
+ fn secondary_confirm(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
+ self.delegate.confirm(true, cx);
+ }
+
+ fn on_input_editor_event(
+ &mut self,
+ _: View<Editor>,
+ event: &editor::Event,
+ cx: &mut ViewContext<Self>,
+ ) {
+ if let editor::Event::BufferEdited = event {
+ let query = self.editor.read(cx).text(cx);
+ self.update_matches(query, cx);
+ }
+ }
+
+ pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
+ let update = self.delegate.update_matches(query, cx);
+ self.matches_updated(cx);
+ self.pending_update_matches = Some(cx.spawn(|this, mut cx| async move {
+ update.await;
+ this.update(&mut cx, |this, cx| {
+ this.matches_updated(cx);
+ })
+ .ok()
+ }));
+ }
+
+ fn matches_updated(&mut self, cx: &mut ViewContext<Self>) {
+ let index = self.delegate.selected_index();
+ self.scroll_handle.scroll_to_item(index);
+ self.pending_update_matches = None;
+ cx.notify();
+ }
+}
+
+impl<D: PickerDelegate> Render for Picker<D> {
+ type Element = Div<Self, StatefulInteractivity<Self>, FocusEnabled<Self>>;
+
+ fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
+ div()
+ .context("picker")
+ .id("picker-container")
+ .focusable()
+ .size_full()
+ .on_action(Self::select_next)
+ .on_action(Self::select_prev)
+ .on_action(Self::select_first)
+ .on_action(Self::select_last)
+ .on_action(Self::cancel)
+ .on_action(Self::confirm)
+ .on_action(Self::secondary_confirm)
+ .child(self.editor.clone())
+ .child(
+ uniform_list("candidates", self.delegate.match_count(), {
+ move |this: &mut Self, visible_range, cx| {
+ let selected_ix = this.delegate.selected_index();
+ visible_range
+ .map(|ix| this.delegate.render_match(ix, ix == selected_ix, cx))
+ .collect()
+ }
+ })
+ .track_scroll(self.scroll_handle.clone())
+ .size_full(),
+ )
+ }
+}
@@ -13,9 +13,12 @@ anyhow.workspace = true
# TODO: Remove after diagnosing stack overflow.
backtrace-on-stack-overflow = "0.3.0"
clap = { version = "4.4", features = ["derive", "string"] }
+editor = { package = "editor2", path = "../editor2" }
chrono = "0.4"
+fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
gpui = { package = "gpui2", path = "../gpui2" }
itertools = "0.11.0"
+language = { package = "language2", path = "../language2" }
log.workspace = true
rust-embed.workspace = true
serde.workspace = true
@@ -25,8 +28,10 @@ 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" }
[dev-dependencies]
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }
@@ -1,6 +1,7 @@
mod colors;
mod focus;
mod kitchen_sink;
+mod picker;
mod scroll;
mod text;
mod z_index;
@@ -8,6 +9,7 @@ mod z_index;
pub use colors::*;
pub use focus::*;
pub use kitchen_sink::*;
+pub use picker::*;
pub use scroll::*;
pub use text::*;
pub use z_index::*;
@@ -1,6 +1,6 @@
use gpui::{
actions, div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render,
- StatefulInteraction, StatelessInteractive, Styled, View, VisualContext, WindowContext,
+ StatefulInteractivity, StatelessInteractive, Styled, View, VisualContext, WindowContext,
};
use theme2::ActiveTheme;
@@ -21,7 +21,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();
@@ -38,20 +38,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))
@@ -60,8 +58,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()
@@ -72,20 +70,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()
@@ -94,12 +88,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"),
)
}
@@ -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()
@@ -0,0 +1,214 @@
+use std::sync::Arc;
+
+use fuzzy::StringMatchCandidate;
+use gpui::{
+ div, Component, Div, KeyBinding, ParentElement, Render, StatelessInteractive, Styled, Task,
+ View, VisualContext, WindowContext,
+};
+use picker::{Picker, PickerDelegate};
+use theme2::ActiveTheme;
+
+pub struct PickerStory {
+ picker: View<Picker<Delegate>>,
+}
+
+struct Delegate {
+ candidates: Arc<[StringMatchCandidate]>,
+ matches: Vec<usize>,
+ selected_ix: usize,
+}
+
+impl Delegate {
+ fn new(strings: &[&str]) -> Self {
+ Self {
+ candidates: strings
+ .iter()
+ .copied()
+ .enumerate()
+ .map(|(id, string)| StringMatchCandidate {
+ id,
+ char_bag: string.into(),
+ string: string.into(),
+ })
+ .collect(),
+ matches: vec![],
+ selected_ix: 0,
+ }
+ }
+}
+
+impl PickerDelegate for Delegate {
+ type ListItem = Div<Picker<Self>>;
+
+ fn match_count(&self) -> usize {
+ self.candidates.len()
+ }
+
+ fn render_match(
+ &self,
+ ix: usize,
+ selected: bool,
+ cx: &mut gpui::ViewContext<Picker<Self>>,
+ ) -> Self::ListItem {
+ let colors = cx.theme().colors();
+ let Some(candidate_ix) = self.matches.get(ix) else {
+ return div();
+ };
+ let candidate = self.candidates[*candidate_ix].string.clone();
+
+ 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(candidate)
+ }
+
+ fn selected_index(&self) -> usize {
+ self.selected_ix
+ }
+
+ fn set_selected_index(&mut self, ix: usize, cx: &mut gpui::ViewContext<Picker<Self>>) {
+ self.selected_ix = ix;
+ cx.notify();
+ }
+
+ fn confirm(&mut self, secondary: bool, cx: &mut gpui::ViewContext<Picker<Self>>) {
+ let candidate_ix = self.matches[self.selected_ix];
+ let candidate = self.candidates[candidate_ix].string.clone();
+
+ if secondary {
+ eprintln!("Secondary confirmed {}", candidate)
+ } else {
+ eprintln!("Confirmed {}", candidate)
+ }
+ }
+
+ fn dismissed(&mut self, cx: &mut gpui::ViewContext<Picker<Self>>) {
+ cx.quit();
+ }
+
+ fn update_matches(
+ &mut self,
+ query: String,
+ cx: &mut gpui::ViewContext<Picker<Self>>,
+ ) -> Task<()> {
+ let candidates = self.candidates.clone();
+ self.matches = cx
+ .background_executor()
+ .block(fuzzy::match_strings(
+ &candidates,
+ &query,
+ true,
+ 100,
+ &Default::default(),
+ cx.background_executor().clone(),
+ ))
+ .into_iter()
+ .map(|r| r.candidate_id)
+ .collect();
+ self.selected_ix = 0;
+ Task::ready(())
+ }
+}
+
+impl PickerStory {
+ pub fn new(cx: &mut WindowContext) -> View<Self> {
+ 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")),
+ ]);
+
+ PickerStory {
+ picker: cx.build_view(|cx| {
+ let mut delegate = Delegate::new(&[
+ "Baguette (France)",
+ "Baklava (Turkey)",
+ "Beef Wellington (UK)",
+ "Biryani (India)",
+ "Borscht (Ukraine)",
+ "Bratwurst (Germany)",
+ "Bulgogi (Korea)",
+ "Burrito (USA)",
+ "Ceviche (Peru)",
+ "Chicken Tikka Masala (India)",
+ "Churrasco (Brazil)",
+ "Couscous (North Africa)",
+ "Croissant (France)",
+ "Dim Sum (China)",
+ "Empanada (Argentina)",
+ "Fajitas (Mexico)",
+ "Falafel (Middle East)",
+ "Feijoada (Brazil)",
+ "Fish and Chips (UK)",
+ "Fondue (Switzerland)",
+ "Goulash (Hungary)",
+ "Haggis (Scotland)",
+ "Kebab (Middle East)",
+ "Kimchi (Korea)",
+ "Lasagna (Italy)",
+ "Maple Syrup Pancakes (Canada)",
+ "Moussaka (Greece)",
+ "Pad Thai (Thailand)",
+ "Paella (Spain)",
+ "Pancakes (USA)",
+ "Pasta Carbonara (Italy)",
+ "Pavlova (Australia)",
+ "Peking Duck (China)",
+ "Pho (Vietnam)",
+ "Pierogi (Poland)",
+ "Pizza (Italy)",
+ "Poutine (Canada)",
+ "Pretzel (Germany)",
+ "Ramen (Japan)",
+ "Rendang (Indonesia)",
+ "Sashimi (Japan)",
+ "Satay (Indonesia)",
+ "Shepherd's Pie (Ireland)",
+ "Sushi (Japan)",
+ "Tacos (Mexico)",
+ "Tandoori Chicken (India)",
+ "Tortilla (Spain)",
+ "Tzatziki (Greece)",
+ "Wiener Schnitzel (Austria)",
+ ]);
+ delegate.update_matches("".into(), cx).detach();
+
+ let picker = Picker::new(delegate, cx);
+ picker.focus(cx);
+ picker
+ }),
+ }
+ })
+ }
+}
+
+impl Render for PickerStory {
+ type Element = Div<Self>;
+
+ fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
+ div()
+ .bg(cx.theme().styles.colors.background)
+ .size_full()
+ .child(self.picker.clone())
+ }
+}
@@ -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();
@@ -52,6 +52,7 @@ pub enum ComponentStory {
TrafficLights,
Workspace,
ZIndex,
+ Picker,
}
impl ComponentStory {
@@ -96,6 +97,7 @@ impl ComponentStory {
Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into(),
Self::Workspace => ui::WorkspaceStory::view(cx).into(),
Self::ZIndex => cx.build_view(|_| ZIndexStory).into(),
+ Self::Picker => PickerStory::new(cx).into(),
}
}
}
@@ -72,6 +72,8 @@ fn main() {
ThemeSettings::override_global(theme_settings, cx);
ui::settings::init(cx);
+ language::init(cx);
+ editor::init(cx);
let window = cx.open_window(
WindowOptions {
@@ -186,9 +186,9 @@ pub fn mouse_side(
}
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint {
- let col = GridCol((pos.x / cur_size.cell_width).as_usize());
+ let col = GridCol((cur_size.cell_width / pos.x) as usize);
let col = min(col, cur_size.last_column());
- let line = (pos.y / cur_size.line_height).as_isize() as i32;
+ let line = (cur_size.line_height / pos.y) as i32;
let line = min(line, cur_size.bottommost_line().0);
AlacPoint::new(GridLine(line - display_offset as i32), col)
}
@@ -1121,8 +1121,7 @@ impl Terminal {
None => return,
};
- let scroll_lines =
- (scroll_delta / self.last_content.size.line_height).as_isize() as i32;
+ let scroll_lines = (scroll_delta / self.last_content.size.line_height) as i32;
self.events
.push_back(InternalEvent::Scroll(AlacScroll::Delta(scroll_lines)));
@@ -1280,11 +1279,11 @@ impl Terminal {
}
/* Calculate the appropriate scroll lines */
TouchPhase::Moved => {
- let old_offset = (self.scroll_px / line_height).as_isize() as i32;
+ let old_offset = (self.scroll_px / line_height) as i32;
self.scroll_px += e.delta.pixel_delta(line_height).y * scroll_multiplier;
- let new_offset = (self.scroll_px / line_height).as_isize() as i32;
+ let new_offset = (self.scroll_px / line_height) as i32;
// Whenever we hit the edges, reset our stored scroll to 0
// so we can respond to changes in direction quickly
@@ -1396,9 +1395,9 @@ fn all_search_matches<'a, T>(
}
fn content_index_for_mouse(pos: Point<Pixels>, size: &TerminalSize) -> usize {
- let col = (pos.x / size.cell_width()).round().as_usize();
+ let col = (pos.x / size.cell_width()).round() as usize;
let clamped_col = min(col, size.columns() - 1);
- let row = (pos.y / size.line_height()).round().as_usize();
+ let row = (pos.y / size.line_height()).round() as usize;
let clamped_row = min(row, size.screen_lines() - 1);
clamped_row * size.columns() + clamped_col
}
@@ -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()`
@@ -1,11 +1,8 @@
-use std::{any::TypeId, sync::Arc};
-
+use crate::Workspace;
use gpui::{
- div, AnyView, AppContext, DispatchPhase, Div, ParentElement, Render, StatelessInteractive,
- View, ViewContext,
+ div, AnyView, AppContext, Div, ParentElement, Render, StatelessInteractive, View, ViewContext,
};
-
-use crate::Workspace;
+use std::{any::TypeId, sync::Arc};
pub struct ModalRegistry {
registered_modals: Vec<(TypeId, Box<dyn Fn(Div<Workspace>) -> Div<Workspace>>)>,
@@ -42,15 +39,7 @@ impl ModalRegistry {
let build_view = build_view.clone();
div.on_action(
- move |workspace: &mut Workspace,
- event: &A,
- phase: DispatchPhase,
- cx: &mut ViewContext<Workspace>| {
- dbg!("GOT HERE");
- if phase == DispatchPhase::Capture {
- return;
- }
-
+ move |workspace: &mut Workspace, event: &A, cx: &mut ViewContext<Workspace>| {
let new_modal = (build_view)(workspace, cx);
workspace.modal_layer.update(cx, |modal_layer, _| {
modal_layer.open_modal = Some(new_modal.into());
@@ -37,4 +37,3 @@
((symbol) @comment
(#match? @comment "^#[cC][iIsS]$"))
-
@@ -37,4 +37,3 @@
((symbol) @comment
(#match? @comment "^#[cC][iIsS]$"))
-