Checkpoint

This commit is contained in:
Mikayla 2023-11-06 14:26:10 -08:00
parent 3f7dc59512
commit ea6755b1ca
No known key found for this signature in database
15 changed files with 233 additions and 78 deletions

1
Cargo.lock generated
View file

@ -8542,6 +8542,7 @@ dependencies = [
"gpui2", "gpui2",
"itertools 0.11.0", "itertools 0.11.0",
"log", "log",
"picker2",
"rust-embed", "rust-embed",
"serde", "serde",
"settings2", "settings2",

View file

@ -2834,7 +2834,7 @@ impl PositionMap {
let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left); let previous_valid = self.snapshot.clip_point(exact_unclipped, Bias::Left);
let next_valid = self.snapshot.clip_point(exact_unclipped, Bias::Right); 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; *exact_unclipped.column_mut() += column_overshoot_after_line_end;
PointForPosition { PointForPosition {
previous_valid, previous_valid,

View file

@ -13,6 +13,7 @@ use std::{
atomic::{AtomicUsize, Ordering::SeqCst}, atomic::{AtomicUsize, Ordering::SeqCst},
Arc, Weak, Arc, Weak,
}, },
thread::panicking,
}; };
slotmap::new_key_type! { pub struct EntityId; } 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> { impl<'a, T> Drop for Lease<'a, T> {
fn drop(&mut self) { fn drop(&mut self) {
if self.entity.is_some() { if self.entity.is_some() && !panicking() {
// We don't panic here, because other panics can cause us to drop the lease without ending it cleanly. panic!("Leases must be ended with EntityMap::end_lease")
log::error!("Leases must be ended with EntityMap::end_lease")
} }
} }
} }

View file

@ -6,5 +6,6 @@ mod text;
pub use div::*; pub use div::*;
pub use img::*; pub use img::*;
pub use list::*;
pub use svg::*; pub use svg::*;
pub use text::*; pub use text::*;

View file

@ -2,36 +2,44 @@ use std::ops::Range;
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{AnyElement, Component, Element, ElementId, StyleRefinement, ViewContext}; use crate::{
point, px, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
LayoutId, Pixels, Size, StyleRefinement, Styled, ViewContext,
};
// We want to support uniform and non-uniform height // We want to support uniform and non-uniform height
// We need to make the ID mandatory, to replace the 'state' field // We need to make the ID mandatory, to replace the 'state' field
// Previous implementation measured the first element as early as possible // Previous implementation measured the first element as early as possible
fn list<'a, Id, V, Iter, C>( pub fn list<Id, V, C>(
id: Id, id: Id,
f: impl 'static + FnOnce(&'a mut V, Range<usize>, &'a mut ViewContext<V>) -> Iter, item_count: usize,
f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> SmallVec<[C; 64]>,
) -> List<V> ) -> List<V>
where where
Id: Into<ElementId>, Id: Into<ElementId>,
V: 'static, V: 'static,
Iter: 'a + Iterator<Item = C>,
C: Component<V>, C: Component<V>,
{ {
List { List {
id: id.into(), id: id.into(),
render_items: Box::new(|view, visible_range, cx| { style: Default::default(),
item_count,
render_items: Box::new(move |view, visible_range, cx| {
f(view, visible_range, cx) f(view, visible_range, cx)
.map(|element| element.render()) .into_iter()
.map(|component| component.render())
.collect() .collect()
}), }),
} }
} }
struct List<V> { pub struct List<V> {
id: ElementId, id: ElementId,
style: StyleRefinement,
item_count: usize,
render_items: Box< render_items: Box<
dyn for<'a> FnOnce( dyn for<'a> Fn(
&'a mut V, &'a mut V,
Range<usize>, Range<usize>,
&'a mut ViewContext<V>, &'a mut ViewContext<V>,
@ -39,8 +47,6 @@ struct List<V> {
>, >,
} }
impl<V> List<V> {}
// #[derive(Debug)] // #[derive(Debug)]
// pub enum ScrollTarget { // pub enum ScrollTarget {
// Show(usize), // Show(usize),
@ -48,24 +54,30 @@ impl<V> List<V> {}
// } // }
#[derive(Default)] #[derive(Default)]
struct ListState { pub struct ListState {
scroll_top: f32, scroll_top: f32,
style: StyleRefinement,
// todo // todo
// scroll_to: Option<ScrollTarget>, // scroll_to: Option<ScrollTarget>,
} }
impl<V: 'static> Styled for List<V> {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.style
}
}
impl<V: 'static> Element<V> for List<V> { impl<V: 'static> Element<V> for List<V> {
type ElementState = ListState; type ElementState = ListState;
fn id(&self) -> Option<crate::ElementId> { fn id(&self) -> Option<crate::ElementId> {
Some(self.id) Some(self.id.clone())
} }
fn initialize( fn initialize(
&mut self, &mut self,
_: &mut V, _: &mut V,
element_state: Option<Self::ElementState>, element_state: Option<Self::ElementState>,
_: &mut crate::ViewContext<V>, _: &mut ViewContext<V>,
) -> Self::ElementState { ) -> Self::ElementState {
let element_state = element_state.unwrap_or_default(); let element_state = element_state.unwrap_or_default();
element_state element_state
@ -73,11 +85,11 @@ impl<V: 'static> Element<V> for List<V> {
fn layout( fn layout(
&mut self, &mut self,
view_state: &mut V, _view_state: &mut V,
element_state: &mut Self::ElementState, _element_state: &mut Self::ElementState,
cx: &mut crate::ViewContext<V>, cx: &mut ViewContext<V>,
) -> crate::LayoutId { ) -> LayoutId {
todo!() cx.request_layout(&self.computed_style(), None)
} }
fn paint( fn paint(
@ -85,7 +97,62 @@ impl<V: 'static> Element<V> for List<V> {
bounds: crate::Bounds<crate::Pixels>, bounds: crate::Bounds<crate::Pixels>,
view_state: &mut V, view_state: &mut V,
element_state: &mut Self::ElementState, element_state: &mut Self::ElementState,
cx: &mut crate::ViewContext<V>, 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),
);
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) as usize;
let visible_range = 0..visible_item_count;
let mut items = (self.render_items)(view_state, visible_range, cx);
for (ix, item) in items.iter_mut().enumerate() {
item.initialize(view_state, cx);
item.layout(view_state, cx);
let offset = padded_bounds.origin + point(px(0.), item_height * ix);
cx.with_element_offset(Some(offset), |cx| item.paint(view_state, cx))
}
}
}
}
impl<V> List<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
}
}
impl<V: 'static> Component<V> for List<V> {
fn render(self) -> AnyElement<V> {
AnyElement::new(self)
} }
} }

View file

@ -259,6 +259,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> { impl Size<Length> {
pub fn full() -> Self { pub fn full() -> Self {
Self { Self {
@ -541,6 +559,15 @@ impl Edges<DefiniteLength> {
left: px(0.).into(), 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> { impl Edges<AbsoluteLength> {
@ -672,16 +699,16 @@ impl<T> Copy for Corners<T> where T: Copy + Clone + Default + Debug {}
pub struct Pixels(pub(crate) f32); pub struct Pixels(pub(crate) f32);
impl std::ops::Div for Pixels { impl std::ops::Div for Pixels {
type Output = Self; type Output = f32;
fn div(self, rhs: Self) -> Self::Output { fn div(self, rhs: Self) -> Self::Output {
Self(self.0 / rhs.0) self.0 / rhs.0
} }
} }
impl std::ops::DivAssign for Pixels { impl std::ops::DivAssign for Pixels {
fn div_assign(&mut self, rhs: Self) { fn div_assign(&mut self, rhs: Self) {
self.0 /= rhs.0; *self = Self(self.0 / rhs.0);
} }
} }
@ -732,14 +759,6 @@ impl MulAssign<f32> for Pixels {
impl Pixels { impl Pixels {
pub const MAX: Pixels = Pixels(f32::MAX); 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 { pub fn floor(&self) -> Self {
Self(self.0.floor()) Self(self.0.floor())
} }

View file

@ -1,14 +1,19 @@
use crate::{ use crate::{
self as gpui, hsla, point, px, relative, rems, AlignItems, CursorStyle, DefiniteLength, self as gpui, hsla, point, px, relative, rems, AlignItems, CursorStyle, DefiniteLength,
Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString, Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString,
StyleRefinement, Visibility, Style, StyleRefinement, Visibility,
}; };
use crate::{BoxShadow, TextStyleRefinement}; use crate::{BoxShadow, TextStyleRefinement};
use refineable::Refineable;
use smallvec::smallvec; use smallvec::smallvec;
pub trait Styled { pub trait Styled {
fn style(&mut self) -> &mut StyleRefinement; fn style(&mut self) -> &mut StyleRefinement;
fn computed_style(&mut self) -> Style {
Style::default().refined(self.style().clone())
}
gpui2_macros::style_helpers!(); gpui2_macros::style_helpers!();
/// Sets the size of the element to the full width and height. /// Sets the size of the element to the full width and height.

View file

@ -559,6 +559,12 @@ impl<'a> WindowContext<'a> {
.request_measured_layout(style, rem_size, measure) .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 /// 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 /// 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. /// in order to pass your element its `Bounds` automatically.

View file

@ -20,24 +20,28 @@
use std::ops::Range; use std::ops::Range;
use gpui::{div, AppContext, Component, Div, Element, ParentElement, Render, ViewContext}; use gpui::{
div, list, AppContext, Component, Div, Element, ElementId, ParentElement, Render, ViewContext,
};
pub struct Picker<D> { // pub struct Picker<D> {
delegate: D, // delegate: D,
// query_editor: ViewHandle<Editor>, // query_editor: ViewHandle<Editor>,
// list_state: UniformListState, // list_state: UniformListState,
// max_size: Vector2F, // max_size: Vector2F,
// theme: Arc<Mutex<Box<dyn Fn(&theme::Theme) -> theme::Picker>>>, // theme: Arc<Mutex<Box<dyn Fn(&theme::Theme) -> theme::Picker>>>,
// confirmed: bool, // confirmed: bool,
// pending_update_matches: Option<Task<Option<()>>>, // pending_update_matches: Option<Task<Option<()>>>,
// confirm_on_update: Option<bool>, // confirm_on_update: Option<bool>,
// has_focus: bool, // has_focus: bool,
} // }
pub trait PickerDelegate: Sized + 'static { pub trait PickerDelegate: Sized + 'static {
type ListItem: Element<Picker<Self>>; type ListItem: Component<Self>;
// fn placeholder_text(&self) -> Arc<str>; // fn placeholder_text(&self) -> Arc<str>;
// fn match_count(&self) -> usize;
fn match_count(&self, picker_id: ElementId) -> usize;
// fn selected_index(&self) -> usize; // fn selected_index(&self) -> usize;
// fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>); // fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
// fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>; // fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
@ -51,7 +55,8 @@ pub trait PickerDelegate: Sized + 'static {
active: bool, active: bool,
hovered: bool, hovered: bool,
selected: bool, selected: bool,
cx: &mut ViewContext<Picker<Self>>, picker_id: ElementId,
cx: &mut ViewContext<Self>,
) -> Self::ListItem; ) -> Self::ListItem;
// fn center_selection_after_match_updates(&self) -> bool { // fn center_selection_after_match_updates(&self) -> bool {
@ -75,27 +80,33 @@ pub trait PickerDelegate: Sized + 'static {
// type Event = PickerEvent; // type Event = PickerEvent;
// } // }
impl<D: PickerDelegate> Render for Picker<D> { #[derive(Component)]
type Element = Div<Self>; pub struct Picker<V: PickerDelegate> {
id: ElementId,
phantom: std::marker::PhantomData<V>,
}
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element { impl<V: PickerDelegate> Picker<V> {
div().child(list( pub fn new(id: impl Into<ElementId>) -> Self {
"candidates", Self {
|this: &mut Picker<D>, visible_range, cx| { id: id.into(),
visible_range phantom: std::marker::PhantomData,
.into_iter() }
.map(|ix| this.delegate.render_match(ix, false, false, false, cx))
},
))
} }
} }
fn list<'a, D: PickerDelegate, F, I>(id: &'static str, f: F) -> Div<Picker<D>> impl<V: 'static + PickerDelegate> Picker<V> {
where pub fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
F: FnOnce(&'a mut Picker<D>, Range<usize>, &'a mut ViewContext<Picker<D>>) -> I, div().id(self.id.clone()).child(list(
I: 'a + Iterator<Item = D::ListItem>, "candidates",
{ view.match_count(self.id.clone()),
todo!(); move |this: &mut V, visible_range, cx| {
visible_range
.map(|ix| this.render_match(ix, false, false, false, self.id.clone(), cx))
.collect()
},
))
}
} }
// impl<D: PickerDelegate> View for Picker<D> { // impl<D: PickerDelegate> View for Picker<D> {
@ -213,7 +224,7 @@ where
// cx.add_action(Self::cancel); // cx.add_action(Self::cancel);
// } // }
// pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self { // pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
// let theme = Arc::new(Mutex::new( // let theme = Arc::new(Mutex::new(
// Box::new(|theme: &theme::Theme| theme.picker.clone()) // Box::new(|theme: &theme::Theme| theme.picker.clone())
// as Box<dyn Fn(&theme::Theme) -> theme::Picker>, // as Box<dyn Fn(&theme::Theme) -> theme::Picker>,
@ -247,7 +258,8 @@ where
// }; // };
// this.update_matches(String::new(), cx); // this.update_matches(String::new(), cx);
// this // this
// } // Self { delegate }
// }
// pub fn with_max_size(mut self, width: f32, height: f32) -> Self { // pub fn with_max_size(mut self, width: f32, height: f32) -> Self {
// self.max_size = vec2f(width, height); // self.max_size = vec2f(width, height);

View file

@ -27,6 +27,7 @@ theme = { path = "../theme" }
theme2 = { path = "../theme2" } theme2 = { path = "../theme2" }
ui = { package = "ui2", path = "../ui2", features = ["stories"] } ui = { package = "ui2", path = "../ui2", features = ["stories"] }
util = { path = "../util" } util = { path = "../util" }
picker = { package = "picker2", path = "../picker2" }
[dev-dependencies] [dev-dependencies]
gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] } gpui = { package = "gpui2", path = "../gpui2", features = ["test-support"] }

View file

@ -1,6 +1,7 @@
mod colors; mod colors;
mod focus; mod focus;
mod kitchen_sink; mod kitchen_sink;
mod picker;
mod scroll; mod scroll;
mod text; mod text;
mod z_index; mod z_index;
@ -8,6 +9,7 @@ mod z_index;
pub use colors::*; pub use colors::*;
pub use focus::*; pub use focus::*;
pub use kitchen_sink::*; pub use kitchen_sink::*;
pub use picker::*;
pub use scroll::*; pub use scroll::*;
pub use text::*; pub use text::*;
pub use z_index::*; pub use z_index::*;

View file

@ -0,0 +1,40 @@
use gpui::{div, Div, ParentElement, Render, View, VisualContext, WindowContext};
use picker::{Picker, PickerDelegate};
pub struct PickerStory {
// picker: View<Picker<PickerStoryDelegate>>,
}
impl PickerDelegate for PickerStory {
type ListItem = Div<Self>;
fn match_count(&self, picker_id: gpui::ElementId) -> usize {
0
}
fn render_match(
&self,
ix: usize,
active: bool,
hovered: bool,
selected: bool,
picker_id: gpui::ElementId,
cx: &mut gpui::ViewContext<Self>,
) -> Self::ListItem {
todo!()
}
}
impl PickerStory {
pub fn new(cx: &mut WindowContext) -> View<Self> {
cx.build_view(|cx| PickerStory {})
}
}
impl Render for PickerStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
div().child(Picker::new("picker_story"))
}
}

View file

@ -51,6 +51,7 @@ pub enum ComponentStory {
TrafficLights, TrafficLights,
Workspace, Workspace,
ZIndex, ZIndex,
Picker,
} }
impl ComponentStory { impl ComponentStory {
@ -94,6 +95,7 @@ impl ComponentStory {
Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into(), Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into(),
Self::Workspace => ui::WorkspaceStory::view(cx).into(), Self::Workspace => ui::WorkspaceStory::view(cx).into(),
Self::ZIndex => cx.build_view(|_| ZIndexStory).into(), Self::ZIndex => cx.build_view(|_| ZIndexStory).into(),
Self::Picker => PickerStory::new(cx).into(),
} }
} }
} }

View file

@ -186,9 +186,9 @@ pub fn mouse_side(
} }
pub fn grid_point(pos: Point<Pixels>, cur_size: TerminalSize, display_offset: usize) -> AlacPoint { 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 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); let line = min(line, cur_size.bottommost_line().0);
AlacPoint::new(GridLine(line - display_offset as i32), col) AlacPoint::new(GridLine(line - display_offset as i32), col)
} }

View file

@ -1121,8 +1121,7 @@ impl Terminal {
None => return, None => return,
}; };
let scroll_lines = let scroll_lines = (scroll_delta / self.last_content.size.line_height) as i32;
(scroll_delta / self.last_content.size.line_height).as_isize() as i32;
self.events self.events
.push_back(InternalEvent::Scroll(AlacScroll::Delta(scroll_lines))); .push_back(InternalEvent::Scroll(AlacScroll::Delta(scroll_lines)));
@ -1280,11 +1279,11 @@ impl Terminal {
} }
/* Calculate the appropriate scroll lines */ /* Calculate the appropriate scroll lines */
TouchPhase::Moved => { 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; 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 // Whenever we hit the edges, reset our stored scroll to 0
// so we can respond to changes in direction quickly // 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 { 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 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); let clamped_row = min(row, size.screen_lines() - 1);
clamped_row * size.columns() + clamped_col clamped_row * size.columns() + clamped_col
} }