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",
"itertools 0.11.0",
"log",
"picker2",
"rust-embed",
"serde",
"settings2",

View file

@ -2834,7 +2834,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,

View file

@ -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")
}
}
}

View file

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

View file

@ -2,36 +2,44 @@ use std::ops::Range;
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 need to make the ID mandatory, to replace the 'state' field
// 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,
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>
where
Id: Into<ElementId>,
V: 'static,
Iter: 'a + Iterator<Item = C>,
C: Component<V>,
{
List {
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)
.map(|element| element.render())
.into_iter()
.map(|component| component.render())
.collect()
}),
}
}
struct List<V> {
pub struct List<V> {
id: ElementId,
style: StyleRefinement,
item_count: usize,
render_items: Box<
dyn for<'a> FnOnce(
dyn for<'a> Fn(
&'a mut V,
Range<usize>,
&'a mut ViewContext<V>,
@ -39,8 +47,6 @@ struct List<V> {
>,
}
impl<V> List<V> {}
// #[derive(Debug)]
// pub enum ScrollTarget {
// Show(usize),
@ -48,24 +54,30 @@ impl<V> List<V> {}
// }
#[derive(Default)]
struct ListState {
pub struct ListState {
scroll_top: f32,
style: StyleRefinement,
// todo
// 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> {
type ElementState = ListState;
fn id(&self) -> Option<crate::ElementId> {
Some(self.id)
Some(self.id.clone())
}
fn initialize(
&mut self,
_: &mut V,
element_state: Option<Self::ElementState>,
_: &mut crate::ViewContext<V>,
_: &mut ViewContext<V>,
) -> Self::ElementState {
let element_state = element_state.unwrap_or_default();
element_state
@ -73,11 +85,11 @@ impl<V: 'static> Element<V> for List<V> {
fn layout(
&mut self,
view_state: &mut V,
element_state: &mut Self::ElementState,
cx: &mut crate::ViewContext<V>,
) -> crate::LayoutId {
todo!()
_view_state: &mut V,
_element_state: &mut Self::ElementState,
cx: &mut ViewContext<V>,
) -> LayoutId {
cx.request_layout(&self.computed_style(), None)
}
fn paint(
@ -85,7 +97,62 @@ impl<V: 'static> Element<V> for List<V> {
bounds: crate::Bounds<crate::Pixels>,
view_state: &mut V,
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> {
pub fn full() -> Self {
Self {
@ -541,6 +559,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> {
@ -672,16 +699,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);
}
}
@ -732,14 +759,6 @@ impl MulAssign<f32> for Pixels {
impl Pixels {
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())
}

View file

@ -1,14 +1,19 @@
use crate::{
self as gpui, hsla, point, px, relative, rems, AlignItems, CursorStyle, DefiniteLength,
Display, Fill, FlexDirection, Hsla, JustifyContent, Length, Position, Rems, SharedString,
StyleRefinement, Visibility,
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.

View file

@ -559,6 +559,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.

View file

@ -20,10 +20,12 @@
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> {
delegate: D,
// pub struct Picker<D> {
// delegate: D,
// query_editor: ViewHandle<Editor>,
// list_state: UniformListState,
// max_size: Vector2F,
@ -32,12 +34,14 @@ pub struct Picker<D> {
// pending_update_matches: Option<Task<Option<()>>>,
// confirm_on_update: Option<bool>,
// has_focus: bool,
}
// }
pub trait PickerDelegate: Sized + 'static {
type ListItem: Element<Picker<Self>>;
type ListItem: Component<Self>;
// 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 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<()>;
@ -51,7 +55,8 @@ pub trait PickerDelegate: Sized + 'static {
active: bool,
hovered: bool,
selected: bool,
cx: &mut ViewContext<Picker<Self>>,
picker_id: ElementId,
cx: &mut ViewContext<Self>,
) -> Self::ListItem;
// fn center_selection_after_match_updates(&self) -> bool {
@ -75,29 +80,35 @@ pub trait PickerDelegate: Sized + 'static {
// type Event = PickerEvent;
// }
impl<D: PickerDelegate> Render for Picker<D> {
type Element = Div<Self>;
#[derive(Component)]
pub struct Picker<V: PickerDelegate> {
id: ElementId,
phantom: std::marker::PhantomData<V>,
}
fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
div().child(list(
impl<V: PickerDelegate> Picker<V> {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
id: id.into(),
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().id(self.id.clone()).child(list(
"candidates",
|this: &mut Picker<D>, visible_range, cx| {
view.match_count(self.id.clone()),
move |this: &mut V, visible_range, cx| {
visible_range
.into_iter()
.map(|ix| this.delegate.render_match(ix, false, false, false, cx))
.map(|ix| this.render_match(ix, false, false, false, self.id.clone(), cx))
.collect()
},
))
}
}
fn list<'a, D: PickerDelegate, F, I>(id: &'static str, f: F) -> Div<Picker<D>>
where
F: FnOnce(&'a mut Picker<D>, Range<usize>, &'a mut ViewContext<Picker<D>>) -> I,
I: 'a + Iterator<Item = D::ListItem>,
{
todo!();
}
// impl<D: PickerDelegate> View for Picker<D> {
// fn ui_name() -> &'static str {
// "Picker"
@ -247,6 +258,7 @@ where
// };
// this.update_matches(String::new(), cx);
// this
// Self { delegate }
// }
// pub fn with_max_size(mut self, width: f32, height: f32) -> Self {

View file

@ -27,6 +27,7 @@ theme = { path = "../theme" }
theme2 = { path = "../theme2" }
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"] }

View file

@ -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::*;

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,
Workspace,
ZIndex,
Picker,
}
impl ComponentStory {
@ -94,6 +95,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(),
}
}
}

View file

@ -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)
}

View file

@ -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
}