Document / lockdown more of GPUI

This commit is contained in:
Mikayla 2024-01-21 14:26:45 -08:00
parent 476de329b3
commit aa57a4cfbc
No known key found for this signature in database
27 changed files with 399 additions and 75 deletions

View file

@ -8,13 +8,13 @@
//! # Element Basics //! # Element Basics
//! //!
//! Elements are constructed by calling [`Render::render()`] on the root view of the window, which //! Elements are constructed by calling [`Render::render()`] on the root view of the window, which
//! which recursively constructs the element tree from the current state of the application. //! which recursively constructs the element tree from the current state of the application,.
//! These elements are then laid out by Taffy, and painted to the screen according to their own //! These elements are then laid out by Taffy, and painted to the screen according to their own
//! implementation of [`Element::paint()`]. Before the start of the next frame, the entire element //! implementation of [`Element::paint()`]. Before the start of the next frame, the entire element
//! tree and any callbacks they have registered with GPUI are dropped and the process repeats. //! tree and any callbacks they have registered with GPUI are dropped and the process repeats.
//! //!
//! But some state is too simple and voluminous to store in every view that needs it, e.g. //! But some state is too simple and voluminous to store in every view that needs it, e.g.
//! whether a hover has been started or not. For this, GPUI provides the [`Element::State`], type. //! whether a hover has been started or not. For this, GPUI provides the [`Element::State`], associated type.
//! If an element returns an [`ElementId`] from [`IntoElement::element_id()`], and that element id //! If an element returns an [`ElementId`] from [`IntoElement::element_id()`], and that element id
//! appears in the same place relative to other views and ElementIds in the frame, then the previous //! appears in the same place relative to other views and ElementIds in the frame, then the previous
//! frame's state will be passed to the element's layout and paint methods. //! frame's state will be passed to the element's layout and paint methods.
@ -30,7 +30,7 @@
//! //!
//! However, most of the time, you won't need to implement your own elements. GPUI provides a number of //! However, most of the time, you won't need to implement your own elements. GPUI provides a number of
//! elements that should cover most common use cases out of the box and it's recommended that you use those //! elements that should cover most common use cases out of the box and it's recommended that you use those
//! to construct `components`, using the `RenderOnce` trait and the `#[derive(IntoElement)]` macro. Only implement //! to construct `components`, using the [`RenderOnce`] trait and the `#[derive(IntoElement)]` macro. Only implement
//! elements when you need to take manual control of the layout and painting process, such as when using //! elements when you need to take manual control of the layout and painting process, such as when using
//! your own custom layout algorithm or rendering a code editor. //! your own custom layout algorithm or rendering a code editor.

View file

@ -9,11 +9,15 @@ use futures::FutureExt;
use media::core_video::CVImageBuffer; use media::core_video::CVImageBuffer;
use util::ResultExt; use util::ResultExt;
/// A source of image content.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum ImageSource { pub enum ImageSource {
/// Image content will be loaded from provided URI at render time. /// Image content will be loaded from provided URI at render time.
Uri(SharedUrl), Uri(SharedUrl),
/// Cached image data
Data(Arc<ImageData>), Data(Arc<ImageData>),
// TODO: move surface definitions into mac platform module
/// A CoreVideo image buffer
Surface(CVImageBuffer), Surface(CVImageBuffer),
} }
@ -47,12 +51,14 @@ impl From<CVImageBuffer> for ImageSource {
} }
} }
/// An image element.
pub struct Img { pub struct Img {
interactivity: Interactivity, interactivity: Interactivity,
source: ImageSource, source: ImageSource,
grayscale: bool, grayscale: bool,
} }
/// Create a new image element.
pub fn img(source: impl Into<ImageSource>) -> Img { pub fn img(source: impl Into<ImageSource>) -> Img {
Img { Img {
interactivity: Interactivity::default(), interactivity: Interactivity::default(),
@ -62,6 +68,7 @@ pub fn img(source: impl Into<ImageSource>) -> Img {
} }
impl Img { impl Img {
/// Set the image to be displayed in grayscale.
pub fn grayscale(mut self, grayscale: bool) -> Self { pub fn grayscale(mut self, grayscale: bool) -> Self {
self.grayscale = grayscale; self.grayscale = grayscale;
self self

View file

@ -1,3 +1,11 @@
//! A list element that can be used to render a large number of differently sized elements
//! efficiently. Clients of this API need to ensure that elements outside of the scrolled
//! area do not change their height for this element to function correctly. In order to minimize
//! re-renders, this element's state is stored intrusively on your own views, so that your code
//! can coordinate directly with the list element's cached state.
//!
//! If all of your elements are the same height, see [`UniformList`] for a simpler API
use crate::{ use crate::{
point, px, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask, point, px, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
DispatchPhase, Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style, DispatchPhase, Element, IntoElement, Pixels, Point, ScrollWheelEvent, Size, Style,
@ -8,6 +16,7 @@ use refineable::Refineable as _;
use std::{cell::RefCell, ops::Range, rc::Rc}; use std::{cell::RefCell, ops::Range, rc::Rc};
use sum_tree::{Bias, SumTree}; use sum_tree::{Bias, SumTree};
/// Construct a new list element
pub fn list(state: ListState) -> List { pub fn list(state: ListState) -> List {
List { List {
state, state,
@ -15,11 +24,13 @@ pub fn list(state: ListState) -> List {
} }
} }
/// A list element
pub struct List { pub struct List {
state: ListState, state: ListState,
style: StyleRefinement, style: StyleRefinement,
} }
/// The list state that views must hold on behalf of the list element.
#[derive(Clone)] #[derive(Clone)]
pub struct ListState(Rc<RefCell<StateInner>>); pub struct ListState(Rc<RefCell<StateInner>>);
@ -35,15 +46,24 @@ struct StateInner {
scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>, scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
} }
/// Whether the list is scrolling from top to bottom or bottom to top.
#[derive(Clone, Copy, Debug, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ListAlignment { pub enum ListAlignment {
/// The list is scrolling from top to bottom, like most lists.
Top, Top,
/// The list is scrolling from bottom to top, like a chat log.
Bottom, Bottom,
} }
/// A scroll event that has been converted to be in terms of the list's items.
pub struct ListScrollEvent { pub struct ListScrollEvent {
/// The range of items currently visible in the list, after applying the scroll event.
pub visible_range: Range<usize>, pub visible_range: Range<usize>,
/// The number of items that are currently visible in the list, after applying the scroll event.
pub count: usize, pub count: usize,
/// Whether the list has been scrolled.
pub is_scrolled: bool, pub is_scrolled: bool,
} }
@ -74,6 +94,11 @@ struct UnrenderedCount(usize);
struct Height(Pixels); struct Height(Pixels);
impl ListState { impl ListState {
/// Construct a new list state, for storage on a view.
///
/// the overdraw parameter controls how much extra space is rendered
/// above and below the visible area. This can help ensure that the list
/// doesn't flicker or pop in when scrolling.
pub fn new<F>( pub fn new<F>(
element_count: usize, element_count: usize,
orientation: ListAlignment, orientation: ListAlignment,
@ -111,10 +136,13 @@ impl ListState {
.extend((0..element_count).map(|_| ListItem::Unrendered), &()); .extend((0..element_count).map(|_| ListItem::Unrendered), &());
} }
/// The number of items in this list.
pub fn item_count(&self) -> usize { pub fn item_count(&self) -> usize {
self.0.borrow().items.summary().count self.0.borrow().items.summary().count
} }
/// Register with the list state that the items in `old_range` have been replaced
/// by `count` new items that must be recalculated.
pub fn splice(&self, old_range: Range<usize>, count: usize) { pub fn splice(&self, old_range: Range<usize>, count: usize) {
let state = &mut *self.0.borrow_mut(); let state = &mut *self.0.borrow_mut();
@ -141,6 +169,7 @@ impl ListState {
state.items = new_heights; state.items = new_heights;
} }
/// Set a handler that will be called when the list is scrolled.
pub fn set_scroll_handler( pub fn set_scroll_handler(
&self, &self,
handler: impl FnMut(&ListScrollEvent, &mut WindowContext) + 'static, handler: impl FnMut(&ListScrollEvent, &mut WindowContext) + 'static,
@ -148,10 +177,12 @@ impl ListState {
self.0.borrow_mut().scroll_handler = Some(Box::new(handler)) self.0.borrow_mut().scroll_handler = Some(Box::new(handler))
} }
/// Get the current scroll offset, in terms of the list's items.
pub fn logical_scroll_top(&self) -> ListOffset { pub fn logical_scroll_top(&self) -> ListOffset {
self.0.borrow().logical_scroll_top() self.0.borrow().logical_scroll_top()
} }
/// Scroll the list to the given offset
pub fn scroll_to(&self, mut scroll_top: ListOffset) { pub fn scroll_to(&self, mut scroll_top: ListOffset) {
let state = &mut *self.0.borrow_mut(); let state = &mut *self.0.borrow_mut();
let item_count = state.items.summary().count; let item_count = state.items.summary().count;
@ -163,6 +194,7 @@ impl ListState {
state.logical_scroll_top = Some(scroll_top); state.logical_scroll_top = Some(scroll_top);
} }
/// Scroll the list to the given item, such that the item is fully visible.
pub fn scroll_to_reveal_item(&self, ix: usize) { pub fn scroll_to_reveal_item(&self, ix: usize) {
let state = &mut *self.0.borrow_mut(); let state = &mut *self.0.borrow_mut();
@ -193,7 +225,8 @@ impl ListState {
state.logical_scroll_top = Some(scroll_top); state.logical_scroll_top = Some(scroll_top);
} }
/// Get the bounds for the given item in window coordinates. /// Get the bounds for the given item in window coordinates, if it's
/// been rendered.
pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> { pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
let state = &*self.0.borrow(); let state = &*self.0.borrow();
@ -310,9 +343,13 @@ impl std::fmt::Debug for ListItem {
} }
} }
/// An offset into the list's items, in terms of the item index and the number
/// of pixels off the top left of the item.
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct ListOffset { pub struct ListOffset {
/// The index of an item in the list
pub item_ix: usize, pub item_ix: usize,
/// The number of pixels to offset from the item index.
pub offset_in_item: Pixels, pub offset_in_item: Pixels,
} }

View file

@ -6,10 +6,13 @@ use crate::{
Point, Size, Style, WindowContext, Point, Size, Style, WindowContext,
}; };
/// The state that the overlay element uses to track its children.
pub struct OverlayState { pub struct OverlayState {
child_layout_ids: SmallVec<[LayoutId; 4]>, child_layout_ids: SmallVec<[LayoutId; 4]>,
} }
/// An overlay element that can be used to display UI that
/// floats on top of other UI elements.
pub struct Overlay { pub struct Overlay {
children: SmallVec<[AnyElement; 2]>, children: SmallVec<[AnyElement; 2]>,
anchor_corner: AnchorCorner, anchor_corner: AnchorCorner,
@ -191,15 +194,21 @@ enum Axis {
Vertical, Vertical,
} }
/// Which algorithm to use when fitting the overlay to be inside the window.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum OverlayFitMode { pub enum OverlayFitMode {
/// Snap the overlay to the window edge
SnapToWindow, SnapToWindow,
/// Switch which corner anchor this overlay is attached to
SwitchAnchor, SwitchAnchor,
} }
/// Which algorithm to use when positioning the overlay.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub enum OverlayPositionMode { pub enum OverlayPositionMode {
/// Position the overlay relative to the window
Window, Window,
/// Position the overlay relative to its parent
Local, Local,
} }
@ -226,11 +235,16 @@ impl OverlayPositionMode {
} }
} }
/// Which corner of the overlay should be considered the anchor.
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum AnchorCorner { pub enum AnchorCorner {
/// The top left corner
TopLeft, TopLeft,
/// The top right corner
TopRight, TopRight,
/// The bottom left corner
BottomLeft, BottomLeft,
/// The bottom right corner
BottomRight, BottomRight,
} }
@ -255,6 +269,7 @@ impl AnchorCorner {
Bounds { origin, size } Bounds { origin, size }
} }
/// Get the point corresponding to this anchor corner in `bounds`.
pub fn corner(&self, bounds: Bounds<Pixels>) -> Point<Pixels> { pub fn corner(&self, bounds: Bounds<Pixels>) -> Point<Pixels> {
match self { match self {
Self::TopLeft => bounds.origin, Self::TopLeft => bounds.origin,

View file

@ -4,11 +4,13 @@ use crate::{
}; };
use util::ResultExt; use util::ResultExt;
/// An SVG element.
pub struct Svg { pub struct Svg {
interactivity: Interactivity, interactivity: Interactivity,
path: Option<SharedString>, path: Option<SharedString>,
} }
/// Create a new SVG element.
pub fn svg() -> Svg { pub fn svg() -> Svg {
Svg { Svg {
interactivity: Interactivity::default(), interactivity: Interactivity::default(),
@ -17,6 +19,7 @@ pub fn svg() -> Svg {
} }
impl Svg { impl Svg {
/// Set the path to the SVG file for this element.
pub fn path(mut self, path: impl Into<SharedString>) -> Self { pub fn path(mut self, path: impl Into<SharedString>) -> Self {
self.path = Some(path.into()); self.path = Some(path.into());
self self

View file

@ -87,6 +87,7 @@ pub struct StyledText {
} }
impl StyledText { impl StyledText {
/// Construct a new styled text element from the given string.
pub fn new(text: impl Into<SharedString>) -> Self { pub fn new(text: impl Into<SharedString>) -> Self {
StyledText { StyledText {
text: text.into(), text: text.into(),
@ -94,6 +95,8 @@ impl StyledText {
} }
} }
/// Set the styling attributes for the given text, as well as
/// as any ranges of text that have had their style customized.
pub fn with_highlights( pub fn with_highlights(
mut self, mut self,
default_style: &TextStyle, default_style: &TextStyle,
@ -151,6 +154,7 @@ impl IntoElement for StyledText {
} }
} }
#[doc(hidden)]
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct TextState(Arc<Mutex<Option<TextStateInner>>>); pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
@ -290,6 +294,7 @@ impl TextState {
} }
} }
/// A text element that can be interacted with.
pub struct InteractiveText { pub struct InteractiveText {
element_id: ElementId, element_id: ElementId,
text: StyledText, text: StyledText,
@ -305,6 +310,7 @@ struct InteractiveTextClickEvent {
mouse_up_index: usize, mouse_up_index: usize,
} }
#[doc(hidden)]
pub struct InteractiveTextState { pub struct InteractiveTextState {
text_state: TextState, text_state: TextState,
mouse_down_index: Rc<Cell<Option<usize>>>, mouse_down_index: Rc<Cell<Option<usize>>>,
@ -314,6 +320,7 @@ pub struct InteractiveTextState {
/// InteractiveTest is a wrapper around StyledText that adds mouse interactions. /// InteractiveTest is a wrapper around StyledText that adds mouse interactions.
impl InteractiveText { impl InteractiveText {
/// Creates a new InteractiveText from the given text.
pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self { pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self {
Self { Self {
element_id: id.into(), element_id: id.into(),

View file

@ -1,3 +1,9 @@
//! A scrollable list of elements with uniform height, optimized for large lists.
//! Rather than use the full taffy layout system, uniform_list simply measures
//! the first element and then lays out all remaining elements in a line based on that
//! measurement. This is much faster than the full layout system, but only works for
//! elements with uniform height.
use crate::{ use crate::{
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element, point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId, ElementId, InteractiveElement, InteractiveElementState, Interactivity, IntoElement, LayoutId,
@ -53,6 +59,7 @@ where
} }
} }
/// A list element for efficiently laying out and displaying a list of uniform-height elements.
pub struct UniformList { pub struct UniformList {
id: ElementId, id: ElementId,
item_count: usize, item_count: usize,
@ -63,18 +70,22 @@ pub struct UniformList {
scroll_handle: Option<UniformListScrollHandle>, scroll_handle: Option<UniformListScrollHandle>,
} }
/// A handle for controlling the scroll position of a uniform list.
/// This should be stored in your view and passed to the uniform_list on each frame.
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct UniformListScrollHandle { pub struct UniformListScrollHandle {
deferred_scroll_to_item: Rc<RefCell<Option<usize>>>, deferred_scroll_to_item: Rc<RefCell<Option<usize>>>,
} }
impl UniformListScrollHandle { impl UniformListScrollHandle {
/// Create a new scroll handle to bind to a uniform list.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
deferred_scroll_to_item: Rc::new(RefCell::new(None)), deferred_scroll_to_item: Rc::new(RefCell::new(None)),
} }
} }
/// Scroll the list to the given item index.
pub fn scroll_to_item(&mut self, ix: usize) { pub fn scroll_to_item(&mut self, ix: usize) {
self.deferred_scroll_to_item.replace(Some(ix)); self.deferred_scroll_to_item.replace(Some(ix));
} }
@ -86,6 +97,7 @@ impl Styled for UniformList {
} }
} }
#[doc(hidden)]
#[derive(Default)] #[derive(Default)]
pub struct UniformListState { pub struct UniformListState {
interactive: InteractiveElementState, interactive: InteractiveElementState,
@ -262,6 +274,7 @@ impl IntoElement for UniformList {
} }
impl UniformList { impl UniformList {
/// Selects a specific list item for measurement.
pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self { pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
self.item_to_measure_index = item_index.unwrap_or(0); self.item_to_measure_index = item_index.unwrap_or(0);
self self
@ -284,6 +297,7 @@ impl UniformList {
item_to_measure.measure(available_space, cx) item_to_measure.measure(available_space, cx)
} }
/// Track and render scroll state of this list with reference to the given scroll handle.
pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self { pub fn track_scroll(mut self, handle: UniformListScrollHandle) -> Self {
self.scroll_handle = Some(handle); self.scroll_handle = Some(handle);
self self

View file

@ -21,11 +21,15 @@ use waker_fn::waker_fn;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
use rand::rngs::StdRng; use rand::rngs::StdRng;
/// A pointer to the executor that is currently running,
/// for spawning background tasks.
#[derive(Clone)] #[derive(Clone)]
pub struct BackgroundExecutor { pub struct BackgroundExecutor {
dispatcher: Arc<dyn PlatformDispatcher>, dispatcher: Arc<dyn PlatformDispatcher>,
} }
/// A pointer to the executor that is currently running,
/// for spawning tasks on the main thread.
#[derive(Clone)] #[derive(Clone)]
pub struct ForegroundExecutor { pub struct ForegroundExecutor {
dispatcher: Arc<dyn PlatformDispatcher>, dispatcher: Arc<dyn PlatformDispatcher>,
@ -37,11 +41,14 @@ pub struct ForegroundExecutor {
/// It implements [`Future`] so you can `.await` on it. /// It implements [`Future`] so you can `.await` on it.
/// ///
/// If you drop a task it will be cancelled immediately. Calling [`Task::detach`] allows /// If you drop a task it will be cancelled immediately. Calling [`Task::detach`] allows
/// the task to continue running in the background, but with no way to return a value. /// the task to continue running, but with no way to return a value.
#[must_use] #[must_use]
#[derive(Debug)] #[derive(Debug)]
pub enum Task<T> { pub enum Task<T> {
/// A task that is ready to return a value
Ready(Option<T>), Ready(Option<T>),
/// A task that is currently running.
Spawned(async_task::Task<T>), Spawned(async_task::Task<T>),
} }
@ -87,6 +94,8 @@ impl<T> Future for Task<T> {
} }
} }
/// A task label is an opaque identifier that you can use to
/// refer to a task in tests.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct TaskLabel(NonZeroUsize); pub struct TaskLabel(NonZeroUsize);
@ -97,6 +106,7 @@ impl Default for TaskLabel {
} }
impl TaskLabel { impl TaskLabel {
/// Construct a new task label.
pub fn new() -> Self { pub fn new() -> Self {
static NEXT_TASK_LABEL: AtomicUsize = AtomicUsize::new(1); static NEXT_TASK_LABEL: AtomicUsize = AtomicUsize::new(1);
Self(NEXT_TASK_LABEL.fetch_add(1, SeqCst).try_into().unwrap()) Self(NEXT_TASK_LABEL.fetch_add(1, SeqCst).try_into().unwrap())
@ -363,6 +373,7 @@ impl BackgroundExecutor {
/// ForegroundExecutor runs things on the main thread. /// ForegroundExecutor runs things on the main thread.
impl ForegroundExecutor { impl ForegroundExecutor {
/// Creates a new ForegroundExecutor from the given PlatformDispatcher.
pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self { pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
Self { Self {
dispatcher, dispatcher,
@ -411,13 +422,14 @@ impl<'a> Scope<'a> {
} }
} }
/// Spawn a future into this scope.
pub fn spawn<F>(&mut self, f: F) pub fn spawn<F>(&mut self, f: F)
where where
F: Future<Output = ()> + Send + 'a, F: Future<Output = ()> + Send + 'a,
{ {
let tx = self.tx.clone().unwrap(); let tx = self.tx.clone().unwrap();
// Safety: The 'a lifetime is guaranteed to outlive any of these futures because // SAFETY: The 'a lifetime is guaranteed to outlive any of these futures because
// dropping this `Scope` blocks until all of the futures have resolved. // dropping this `Scope` blocks until all of the futures have resolved.
let f = unsafe { let f = unsafe {
mem::transmute::< mem::transmute::<

View file

@ -1,3 +1,33 @@
//! # Welcome to GPUI!
//!
//! GPUI is a hybrid immedate and retained mode, GPU accelerated, UI framework
//! for Rust, designed to support a wide variety of applications. GPUI is currently
//! being actively developed and improved for the [Zed code editor](https://zed.dev/), and new versions
//! will have breaking changes. You'll probably need to use the latest stable version
//! of rust to use GPUI.
//!
//! # Getting started with GPUI
//!
//! TODO: Write a code sample showing how to create a window and render a simple
//! div
//!
//! # Drawing interesting things
//!
//! TODO: Expand demo to show how to draw a more interesting scene, with
//! a counter to store state and a button to increment it.
//!
//! # Interacting with your application state
//!
//! TODO: Expand demo to show GPUI entity interactions, like subscriptions and entities
//! maybe make a network request to show async stuff?
//!
//! # Conclusion
//!
//! TODO: Wrap up with a conclusion and links to other places? Zed / GPUI website?
//! Discord for chatting about it? Other tutorials or references?
#![deny(missing_docs)]
#[macro_use] #[macro_use]
mod action; mod action;
mod app; mod app;
@ -58,10 +88,10 @@ pub use elements::*;
pub use executor::*; pub use executor::*;
pub use geometry::*; pub use geometry::*;
pub use gpui_macros::{register_action, test, IntoElement, Render}; pub use gpui_macros::{register_action, test, IntoElement, Render};
pub use image_cache::*; use image_cache::*;
pub use input::*; pub use input::*;
pub use interactive::*; pub use interactive::*;
pub use key_dispatch::*; use key_dispatch::*;
pub use keymap::*; pub use keymap::*;
pub use platform::*; pub use platform::*;
pub use refineable::*; pub use refineable::*;
@ -73,7 +103,7 @@ pub use smol::Timer;
pub use style::*; pub use style::*;
pub use styled::*; pub use styled::*;
pub use subscription::*; pub use subscription::*;
pub use svg_renderer::*; use svg_renderer::*;
pub use taffy::{AvailableSpace, LayoutId}; pub use taffy::{AvailableSpace, LayoutId};
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub use test::*; pub use test::*;
@ -82,20 +112,23 @@ pub use util::arc_cow::ArcCow;
pub use view::*; pub use view::*;
pub use window::*; pub use window::*;
use std::{ use std::{any::Any, borrow::BorrowMut};
any::{Any, TypeId},
borrow::BorrowMut,
};
use taffy::TaffyLayoutEngine; use taffy::TaffyLayoutEngine;
/// The context trait, allows the different contexts in GPUI to be used
/// interchangeably for certain operations.
pub trait Context { pub trait Context {
/// The result type for this context, used for async contexts that
/// can't hold a direct reference to the application context.
type Result<T>; type Result<T>;
/// Create a new model in the app context.
fn new_model<T: 'static>( fn new_model<T: 'static>(
&mut self, &mut self,
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T, build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
) -> Self::Result<Model<T>>; ) -> Self::Result<Model<T>>;
/// Update a model in the app context.
fn update_model<T, R>( fn update_model<T, R>(
&mut self, &mut self,
handle: &Model<T>, handle: &Model<T>,
@ -104,6 +137,7 @@ pub trait Context {
where where
T: 'static; T: 'static;
/// Read a model from the app context.
fn read_model<T, R>( fn read_model<T, R>(
&self, &self,
handle: &Model<T>, handle: &Model<T>,
@ -112,10 +146,12 @@ pub trait Context {
where where
T: 'static; T: 'static;
/// Update a window for the given handle.
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T> fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
where where
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T; F: FnOnce(AnyView, &mut WindowContext<'_>) -> T;
/// Read a window off of the application context.
fn read_window<T, R>( fn read_window<T, R>(
&self, &self,
window: &WindowHandle<T>, window: &WindowHandle<T>,
@ -125,7 +161,10 @@ pub trait Context {
T: 'static; T: 'static;
} }
/// This trait is used for the different visual contexts in GPUI that
/// require a window to be present.
pub trait VisualContext: Context { pub trait VisualContext: Context {
/// Construct a new view in the window referenced by this context.
fn new_view<V>( fn new_view<V>(
&mut self, &mut self,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V, build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
@ -133,12 +172,14 @@ pub trait VisualContext: Context {
where where
V: 'static + Render; V: 'static + Render;
/// Update a view with the given callback
fn update_view<V: 'static, R>( fn update_view<V: 'static, R>(
&mut self, &mut self,
view: &View<V>, view: &View<V>,
update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R, update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
) -> Self::Result<R>; ) -> Self::Result<R>;
/// Replace the root view of a window with a new view.
fn replace_root_view<V>( fn replace_root_view<V>(
&mut self, &mut self,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V, build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
@ -146,38 +187,47 @@ pub trait VisualContext: Context {
where where
V: 'static + Render; V: 'static + Render;
/// Focus a view in the window, if it implements the [`FocusableView`] trait.
fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()> fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
where where
V: FocusableView; V: FocusableView;
/// Dismiss a view in the window, if it implements the [`ManagedView`] trait.
fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()> fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
where where
V: ManagedView; V: ManagedView;
} }
/// A trait that allows models and views to be interchangeable in certain operations
pub trait Entity<T>: Sealed { pub trait Entity<T>: Sealed {
/// The weak reference type for this entity.
type Weak: 'static; type Weak: 'static;
/// The ID for this entity
fn entity_id(&self) -> EntityId; fn entity_id(&self) -> EntityId;
/// Downgrade this entity to a weak reference.
fn downgrade(&self) -> Self::Weak; fn downgrade(&self) -> Self::Weak;
/// Upgrade this entity from a weak reference.
fn upgrade_from(weak: &Self::Weak) -> Option<Self> fn upgrade_from(weak: &Self::Weak) -> Option<Self>
where where
Self: Sized; Self: Sized;
} }
/// A trait for tying together the types of a GPUI entity and the events it can
/// emit.
pub trait EventEmitter<E: Any>: 'static {} pub trait EventEmitter<E: Any>: 'static {}
pub enum GlobalKey { /// A helper trait for auto-implementing certain methods on contexts that
Numeric(usize), /// can be used interchangeably.
View(EntityId),
Type(TypeId),
}
pub trait BorrowAppContext { pub trait BorrowAppContext {
/// Run a closure with a text style pushed onto the context.
fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R fn with_text_style<F, R>(&mut self, style: Option<TextStyleRefinement>, f: F) -> R
where where
F: FnOnce(&mut Self) -> R; F: FnOnce(&mut Self) -> R;
/// Set a global value on the context.
fn set_global<T: 'static>(&mut self, global: T); fn set_global<T: 'static>(&mut self, global: T);
} }
@ -204,7 +254,9 @@ where
} }
} }
/// A flatten equivalent for anyhow `Result`s.
pub trait Flatten<T> { pub trait Flatten<T> {
/// Convert this type into a simple `Result<T>`.
fn flatten(self) -> Result<T>; fn flatten(self) -> Result<T>;
} }

View file

@ -11,12 +11,12 @@ use thiserror::Error;
use util::http::{self, HttpClient}; use util::http::{self, HttpClient};
#[derive(PartialEq, Eq, Hash, Clone)] #[derive(PartialEq, Eq, Hash, Clone)]
pub struct RenderImageParams { pub(crate) struct RenderImageParams {
pub(crate) image_id: ImageId, pub(crate) image_id: ImageId,
} }
#[derive(Debug, Error, Clone)] #[derive(Debug, Error, Clone)]
pub enum Error { pub(crate) enum Error {
#[error("http error: {0}")] #[error("http error: {0}")]
Client(#[from] http::Error), Client(#[from] http::Error),
#[error("IO error: {0}")] #[error("IO error: {0}")]
@ -42,7 +42,7 @@ impl From<ImageError> for Error {
} }
} }
pub struct ImageCache { pub(crate) struct ImageCache {
client: Arc<dyn HttpClient>, client: Arc<dyn HttpClient>,
images: Arc<Mutex<HashMap<SharedUrl, FetchImageFuture>>>, images: Arc<Mutex<HashMap<SharedUrl, FetchImageFuture>>>,
} }

View file

@ -3,20 +3,33 @@ use std::ops::Range;
/// Implement this trait to allow views to handle textual input when implementing an editor, field, etc. /// Implement this trait to allow views to handle textual input when implementing an editor, field, etc.
/// ///
/// Once your view `V` implements this trait, you can use it to construct an [`ElementInputHandler<V>`]. /// Once your view implements this trait, you can use it to construct an [`ElementInputHandler<V>`].
/// This input handler can then be assigned during paint by calling [`WindowContext::handle_input`]. /// This input handler can then be assigned during paint by calling [`WindowContext::handle_input`].
///
/// See [`InputHandler`] for details on how to implement each method.
pub trait ViewInputHandler: 'static + Sized { pub trait ViewInputHandler: 'static + Sized {
/// See [`InputHandler::text_for_range`] for details
fn text_for_range(&mut self, range: Range<usize>, cx: &mut ViewContext<Self>) fn text_for_range(&mut self, range: Range<usize>, cx: &mut ViewContext<Self>)
-> Option<String>; -> Option<String>;
/// See [`InputHandler::selected_text_range`] for details
fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>; fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
/// See [`InputHandler::marked_text_range`] for details
fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>; fn marked_text_range(&self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
/// See [`InputHandler::unmark_text`] for details
fn unmark_text(&mut self, cx: &mut ViewContext<Self>); fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
/// See [`InputHandler::replace_text_in_range`] for details
fn replace_text_in_range( fn replace_text_in_range(
&mut self, &mut self,
range: Option<Range<usize>>, range: Option<Range<usize>>,
text: &str, text: &str,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
); );
/// See [`InputHandler::replace_and_mark_text_in_range`] for details
fn replace_and_mark_text_in_range( fn replace_and_mark_text_in_range(
&mut self, &mut self,
range: Option<Range<usize>>, range: Option<Range<usize>>,
@ -24,6 +37,8 @@ pub trait ViewInputHandler: 'static + Sized {
new_selected_range: Option<Range<usize>>, new_selected_range: Option<Range<usize>>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
); );
/// See [`InputHandler::bounds_for_range`] for details
fn bounds_for_range( fn bounds_for_range(
&mut self, &mut self,
range_utf16: Range<usize>, range_utf16: Range<usize>,

View file

@ -4,15 +4,25 @@ use crate::{
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf}; use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
/// An event from a platform input source.
pub trait InputEvent: Sealed + 'static { pub trait InputEvent: Sealed + 'static {
/// Convert this event into the platform input enum.
fn to_platform_input(self) -> PlatformInput; fn to_platform_input(self) -> PlatformInput;
} }
/// A key event from the platform.
pub trait KeyEvent: InputEvent {} pub trait KeyEvent: InputEvent {}
/// A mouse event from the platform.
pub trait MouseEvent: InputEvent {} pub trait MouseEvent: InputEvent {}
/// The key down event equivalent for the platform.
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct KeyDownEvent { pub struct KeyDownEvent {
/// The keystroke that was generated.
pub keystroke: Keystroke, pub keystroke: Keystroke,
/// Whether the key is currently held down.
pub is_held: bool, pub is_held: bool,
} }
@ -24,8 +34,10 @@ impl InputEvent for KeyDownEvent {
} }
impl KeyEvent for KeyDownEvent {} impl KeyEvent for KeyDownEvent {}
/// The key up event equivalent for the platform.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct KeyUpEvent { pub struct KeyUpEvent {
/// The keystroke that was released.
pub keystroke: Keystroke, pub keystroke: Keystroke,
} }
@ -37,8 +49,10 @@ impl InputEvent for KeyUpEvent {
} }
impl KeyEvent for KeyUpEvent {} impl KeyEvent for KeyUpEvent {}
/// The modifiers changed event equivalent for the platform.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct ModifiersChangedEvent { pub struct ModifiersChangedEvent {
/// The new state of the modifier keys
pub modifiers: Modifiers, pub modifiers: Modifiers,
} }
@ -62,17 +76,28 @@ impl Deref for ModifiersChangedEvent {
/// Based on the winit enum of the same name. /// Based on the winit enum of the same name.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, Default)]
pub enum TouchPhase { pub enum TouchPhase {
/// The touch started.
Started, Started,
/// The touch event is moving.
#[default] #[default]
Moved, Moved,
/// The touch phase has ended
Ended, Ended,
} }
/// A mouse down event from the platform
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct MouseDownEvent { pub struct MouseDownEvent {
/// Which mouse button was pressed.
pub button: MouseButton, pub button: MouseButton,
/// The position of the mouse on the window.
pub position: Point<Pixels>, pub position: Point<Pixels>,
/// The modifiers that were held down when the mouse was pressed.
pub modifiers: Modifiers, pub modifiers: Modifiers,
/// The number of times the button has been clicked.
pub click_count: usize, pub click_count: usize,
} }
@ -84,11 +109,19 @@ impl InputEvent for MouseDownEvent {
} }
impl MouseEvent for MouseDownEvent {} impl MouseEvent for MouseDownEvent {}
/// A mouse up event from the platform
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct MouseUpEvent { pub struct MouseUpEvent {
/// Which mouse button was released.
pub button: MouseButton, pub button: MouseButton,
/// The position of the mouse on the window.
pub position: Point<Pixels>, pub position: Point<Pixels>,
/// The modifiers that were held down when the mouse was released.
pub modifiers: Modifiers, pub modifiers: Modifiers,
/// The number of times the button has been clicked.
pub click_count: usize, pub click_count: usize,
} }
@ -100,21 +133,34 @@ impl InputEvent for MouseUpEvent {
} }
impl MouseEvent for MouseUpEvent {} impl MouseEvent for MouseUpEvent {}
/// A click event, generated when a mouse button is pressed and released.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct ClickEvent { pub struct ClickEvent {
/// The mouse event when the button was pressed.
pub down: MouseDownEvent, pub down: MouseDownEvent,
/// The mouse event when the button was released.
pub up: MouseUpEvent, pub up: MouseUpEvent,
} }
/// An enum representing the mouse button that was pressed.
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] #[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
pub enum MouseButton { pub enum MouseButton {
/// The left mouse button.
Left, Left,
/// The right mouse button.
Right, Right,
/// The middle mouse button.
Middle, Middle,
/// A navigation button, such as back or forward.
Navigate(NavigationDirection), Navigate(NavigationDirection),
} }
impl MouseButton { impl MouseButton {
/// Get all the mouse buttons in a list.
pub fn all() -> Vec<Self> { pub fn all() -> Vec<Self> {
vec![ vec![
MouseButton::Left, MouseButton::Left,
@ -132,9 +178,13 @@ impl Default for MouseButton {
} }
} }
/// A navigation direction, such as back or forward.
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] #[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
pub enum NavigationDirection { pub enum NavigationDirection {
/// The back button.
Back, Back,
/// The forward button.
Forward, Forward,
} }
@ -144,10 +194,16 @@ impl Default for NavigationDirection {
} }
} }
/// A mouse move event from the platform
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct MouseMoveEvent { pub struct MouseMoveEvent {
/// The position of the mouse on the window.
pub position: Point<Pixels>, pub position: Point<Pixels>,
/// The mouse button that was pressed, if any.
pub pressed_button: Option<MouseButton>, pub pressed_button: Option<MouseButton>,
/// The modifiers that were held down when the mouse was moved.
pub modifiers: Modifiers, pub modifiers: Modifiers,
} }
@ -160,16 +216,25 @@ impl InputEvent for MouseMoveEvent {
impl MouseEvent for MouseMoveEvent {} impl MouseEvent for MouseMoveEvent {}
impl MouseMoveEvent { impl MouseMoveEvent {
/// Returns true if the left mouse button is currently held down.
pub fn dragging(&self) -> bool { pub fn dragging(&self) -> bool {
self.pressed_button == Some(MouseButton::Left) self.pressed_button == Some(MouseButton::Left)
} }
} }
/// A mouse wheel event from the platform
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct ScrollWheelEvent { pub struct ScrollWheelEvent {
/// The position of the mouse on the window.
pub position: Point<Pixels>, pub position: Point<Pixels>,
/// The change in scroll wheel position for this event.
pub delta: ScrollDelta, pub delta: ScrollDelta,
/// The modifiers that were held down when the mouse was moved.
pub modifiers: Modifiers, pub modifiers: Modifiers,
/// The phase of the touch event.
pub touch_phase: TouchPhase, pub touch_phase: TouchPhase,
} }
@ -189,9 +254,12 @@ impl Deref for ScrollWheelEvent {
} }
} }
/// The scroll delta for a scroll wheel event.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub enum ScrollDelta { pub enum ScrollDelta {
/// An exact scroll delta in pixels.
Pixels(Point<Pixels>), Pixels(Point<Pixels>),
/// An inexact scroll delta in lines.
Lines(Point<f32>), Lines(Point<f32>),
} }
@ -202,6 +270,7 @@ impl Default for ScrollDelta {
} }
impl ScrollDelta { impl ScrollDelta {
/// Returns true if this is a precise scroll delta in pixels.
pub fn precise(&self) -> bool { pub fn precise(&self) -> bool {
match self { match self {
ScrollDelta::Pixels(_) => true, ScrollDelta::Pixels(_) => true,
@ -209,6 +278,7 @@ impl ScrollDelta {
} }
} }
/// Converts this scroll event into exact pixels.
pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> { pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
match self { match self {
ScrollDelta::Pixels(delta) => *delta, ScrollDelta::Pixels(delta) => *delta,
@ -216,6 +286,7 @@ impl ScrollDelta {
} }
} }
/// Combines two scroll deltas into one.
pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta { pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta {
match (self, other) { match (self, other) {
(ScrollDelta::Pixels(px_a), ScrollDelta::Pixels(px_b)) => { (ScrollDelta::Pixels(px_a), ScrollDelta::Pixels(px_b)) => {
@ -231,10 +302,15 @@ impl ScrollDelta {
} }
} }
/// A mouse exit event from the platform, generated when the mouse leaves the window.
/// The position generated should be just outside of the window's bounds.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct MouseExitEvent { pub struct MouseExitEvent {
/// The position of the mouse relative to the window.
pub position: Point<Pixels>, pub position: Point<Pixels>,
/// The mouse button that was pressed, if any.
pub pressed_button: Option<MouseButton>, pub pressed_button: Option<MouseButton>,
/// The modifiers that were held down when the mouse was moved.
pub modifiers: Modifiers, pub modifiers: Modifiers,
} }
@ -254,10 +330,12 @@ impl Deref for MouseExitEvent {
} }
} }
/// A collection of paths from the platform, such as from a file drop.
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>); pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
impl ExternalPaths { impl ExternalPaths {
/// Convert this collection of paths into a slice.
pub fn paths(&self) -> &[PathBuf] { pub fn paths(&self) -> &[PathBuf] {
&self.0 &self.0
} }
@ -269,18 +347,27 @@ impl Render for ExternalPaths {
} }
} }
/// A file drop event from the platform, generated when files are dragged and dropped onto the window.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum FileDropEvent { pub enum FileDropEvent {
/// The files have entered the window.
Entered { Entered {
/// The position of the mouse relative to the window.
position: Point<Pixels>, position: Point<Pixels>,
/// The paths of the files that are being dragged.
paths: ExternalPaths, paths: ExternalPaths,
}, },
/// The files are being dragged over the window
Pending { Pending {
/// The position of the mouse relative to the window.
position: Point<Pixels>, position: Point<Pixels>,
}, },
/// The files have been dropped onto the window.
Submit { Submit {
/// The position of the mouse relative to the window.
position: Point<Pixels>, position: Point<Pixels>,
}, },
/// The user has stopped dragging the files over the window.
Exited, Exited,
} }
@ -292,40 +379,31 @@ impl InputEvent for FileDropEvent {
} }
impl MouseEvent for FileDropEvent {} impl MouseEvent for FileDropEvent {}
/// An enum corresponding to all kinds of platform input events.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum PlatformInput { pub enum PlatformInput {
/// A key was pressed.
KeyDown(KeyDownEvent), KeyDown(KeyDownEvent),
/// A key was released.
KeyUp(KeyUpEvent), KeyUp(KeyUpEvent),
/// The keyboard modifiers were changed.
ModifiersChanged(ModifiersChangedEvent), ModifiersChanged(ModifiersChangedEvent),
/// The mouse was pressed.
MouseDown(MouseDownEvent), MouseDown(MouseDownEvent),
/// The mouse was released.
MouseUp(MouseUpEvent), MouseUp(MouseUpEvent),
/// The mouse was moved.
MouseMove(MouseMoveEvent), MouseMove(MouseMoveEvent),
/// The mouse exited the window.
MouseExited(MouseExitEvent), MouseExited(MouseExitEvent),
/// The scroll wheel was used.
ScrollWheel(ScrollWheelEvent), ScrollWheel(ScrollWheelEvent),
/// Files were dragged and dropped onto the window.
FileDrop(FileDropEvent), FileDrop(FileDropEvent),
} }
impl PlatformInput { impl PlatformInput {
pub fn position(&self) -> Option<Point<Pixels>> { pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
match self {
PlatformInput::KeyDown { .. } => None,
PlatformInput::KeyUp { .. } => None,
PlatformInput::ModifiersChanged { .. } => None,
PlatformInput::MouseDown(event) => Some(event.position),
PlatformInput::MouseUp(event) => Some(event.position),
PlatformInput::MouseMove(event) => Some(event.position),
PlatformInput::MouseExited(event) => Some(event.position),
PlatformInput::ScrollWheel(event) => Some(event.position),
PlatformInput::FileDrop(FileDropEvent::Exited) => None,
PlatformInput::FileDrop(
FileDropEvent::Entered { position, .. }
| FileDropEvent::Pending { position, .. }
| FileDropEvent::Submit { position, .. },
) => Some(*position),
}
}
pub fn mouse_event(&self) -> Option<&dyn Any> {
match self { match self {
PlatformInput::KeyDown { .. } => None, PlatformInput::KeyDown { .. } => None,
PlatformInput::KeyUp { .. } => None, PlatformInput::KeyUp { .. } => None,
@ -339,7 +417,7 @@ impl PlatformInput {
} }
} }
pub fn keyboard_event(&self) -> Option<&dyn Any> { pub(crate) fn keyboard_event(&self) -> Option<&dyn Any> {
match self { match self {
PlatformInput::KeyDown(event) => Some(event), PlatformInput::KeyDown(event) => Some(event),
PlatformInput::KeyUp(event) => Some(event), PlatformInput::KeyUp(event) => Some(event),

View file

@ -13,7 +13,7 @@ use std::{
}; };
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct DispatchNodeId(usize); pub(crate) struct DispatchNodeId(usize);
pub(crate) struct DispatchTree { pub(crate) struct DispatchTree {
node_stack: Vec<DispatchNodeId>, node_stack: Vec<DispatchNodeId>,

View file

@ -2,6 +2,7 @@ use crate::{Action, KeyBindingContextPredicate, KeyMatch, Keystroke};
use anyhow::Result; use anyhow::Result;
use smallvec::SmallVec; use smallvec::SmallVec;
/// A keybinding and it's associated metadata, from the keymap.
pub struct KeyBinding { pub struct KeyBinding {
pub(crate) action: Box<dyn Action>, pub(crate) action: Box<dyn Action>,
pub(crate) keystrokes: SmallVec<[Keystroke; 2]>, pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
@ -19,10 +20,12 @@ impl Clone for KeyBinding {
} }
impl KeyBinding { impl KeyBinding {
/// Construct a new keybinding from the given data.
pub fn new<A: Action>(keystrokes: &str, action: A, context_predicate: Option<&str>) -> Self { pub fn new<A: Action>(keystrokes: &str, action: A, context_predicate: Option<&str>) -> Self {
Self::load(keystrokes, Box::new(action), context_predicate).unwrap() Self::load(keystrokes, Box::new(action), context_predicate).unwrap()
} }
/// Load a keybinding from the given raw data.
pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> { pub fn load(keystrokes: &str, action: Box<dyn Action>, context: Option<&str>) -> Result<Self> {
let context = if let Some(context) = context { let context = if let Some(context) = context {
Some(KeyBindingContextPredicate::parse(context)?) Some(KeyBindingContextPredicate::parse(context)?)
@ -42,6 +45,7 @@ impl KeyBinding {
}) })
} }
/// Check if the given keystrokes match this binding.
pub fn match_keystrokes(&self, pending_keystrokes: &[Keystroke]) -> KeyMatch { pub fn match_keystrokes(&self, pending_keystrokes: &[Keystroke]) -> KeyMatch {
if self.keystrokes.as_ref().starts_with(pending_keystrokes) { if self.keystrokes.as_ref().starts_with(pending_keystrokes) {
// If the binding is completed, push it onto the matches list // If the binding is completed, push it onto the matches list
@ -55,10 +59,12 @@ impl KeyBinding {
} }
} }
/// Get the keystrokes associated with this binding
pub fn keystrokes(&self) -> &[Keystroke] { pub fn keystrokes(&self) -> &[Keystroke] {
self.keystrokes.as_slice() self.keystrokes.as_slice()
} }
/// Get the action associated with this binding
pub fn action(&self) -> &dyn Action { pub fn action(&self) -> &dyn Action {
self.action.as_ref() self.action.as_ref()
} }

View file

@ -3,6 +3,10 @@ use anyhow::{anyhow, Result};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt; use std::fmt;
/// A datastructure for resolving whether an action should be dispatched
/// at this point in the element tree. Contains a set of identifiers
/// and/or key value pairs representing the current context for the
/// keymap.
#[derive(Clone, Default, Eq, PartialEq, Hash)] #[derive(Clone, Default, Eq, PartialEq, Hash)]
pub struct KeyContext(SmallVec<[ContextEntry; 1]>); pub struct KeyContext(SmallVec<[ContextEntry; 1]>);
@ -21,6 +25,11 @@ impl<'a> TryFrom<&'a str> for KeyContext {
} }
impl KeyContext { impl KeyContext {
/// Parse a key context from a string.
/// The key context format is very simple:
/// - either a single identifier, such as `StatusBar`
/// - or a key value pair, such as `mode = visible`
/// - seperated by whitespace, such as `StatusBar mode = visible`
pub fn parse(source: &str) -> Result<Self> { pub fn parse(source: &str) -> Result<Self> {
let mut context = Self::default(); let mut context = Self::default();
let source = skip_whitespace(source); let source = skip_whitespace(source);
@ -53,14 +62,17 @@ impl KeyContext {
Self::parse_expr(source, context) Self::parse_expr(source, context)
} }
/// Check if this context is empty.
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.0.is_empty() self.0.is_empty()
} }
/// Clear this context.
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.0.clear(); self.0.clear();
} }
/// Extend this context with another context.
pub fn extend(&mut self, other: &Self) { pub fn extend(&mut self, other: &Self) {
for entry in &other.0 { for entry in &other.0 {
if !self.contains(&entry.key) { if !self.contains(&entry.key) {
@ -69,6 +81,7 @@ impl KeyContext {
} }
} }
/// Add an identifier to this context, if it's not already in this context.
pub fn add<I: Into<SharedString>>(&mut self, identifier: I) { pub fn add<I: Into<SharedString>>(&mut self, identifier: I) {
let key = identifier.into(); let key = identifier.into();
@ -77,6 +90,7 @@ impl KeyContext {
} }
} }
/// Set a key value pair in this context, if it's not already set.
pub fn set<S1: Into<SharedString>, S2: Into<SharedString>>(&mut self, key: S1, value: S2) { pub fn set<S1: Into<SharedString>, S2: Into<SharedString>>(&mut self, key: S1, value: S2) {
let key = key.into(); let key = key.into();
if !self.contains(&key) { if !self.contains(&key) {
@ -87,10 +101,12 @@ impl KeyContext {
} }
} }
/// Check if this context contains a given identifier or key.
pub fn contains(&self, key: &str) -> bool { pub fn contains(&self, key: &str) -> bool {
self.0.iter().any(|entry| entry.key.as_ref() == key) self.0.iter().any(|entry| entry.key.as_ref() == key)
} }
/// Get the associated value for a given identifier or key.
pub fn get(&self, key: &str) -> Option<&SharedString> { pub fn get(&self, key: &str) -> Option<&SharedString> {
self.0 self.0
.iter() .iter()
@ -117,20 +133,31 @@ impl fmt::Debug for KeyContext {
} }
} }
/// A datastructure for resolving whether an action should be dispatched
/// Representing a small language for describing which contexts correspond
/// to which actions.
#[derive(Clone, Debug, Eq, PartialEq, Hash)] #[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum KeyBindingContextPredicate { pub enum KeyBindingContextPredicate {
/// A predicate that will match a given identifier.
Identifier(SharedString), Identifier(SharedString),
/// A predicate that will match a given key-value pair.
Equal(SharedString, SharedString), Equal(SharedString, SharedString),
/// A predicate that will match a given key-value pair not being present.
NotEqual(SharedString, SharedString), NotEqual(SharedString, SharedString),
/// A predicate that will match a given predicate appearing below another predicate.
/// in the element tree
Child( Child(
Box<KeyBindingContextPredicate>, Box<KeyBindingContextPredicate>,
Box<KeyBindingContextPredicate>, Box<KeyBindingContextPredicate>,
), ),
/// Predicate that will invert another predicate.
Not(Box<KeyBindingContextPredicate>), Not(Box<KeyBindingContextPredicate>),
/// A predicate that will match if both of its children match.
And( And(
Box<KeyBindingContextPredicate>, Box<KeyBindingContextPredicate>,
Box<KeyBindingContextPredicate>, Box<KeyBindingContextPredicate>,
), ),
/// A predicate that will match if either of its children match.
Or( Or(
Box<KeyBindingContextPredicate>, Box<KeyBindingContextPredicate>,
Box<KeyBindingContextPredicate>, Box<KeyBindingContextPredicate>,
@ -138,6 +165,34 @@ pub enum KeyBindingContextPredicate {
} }
impl KeyBindingContextPredicate { impl KeyBindingContextPredicate {
/// Parse a string in the same format as the keymap's context field.
///
/// A basic equivalence check against a set of identifiers can performed by
/// simply writing a string:
///
/// `StatusBar` -> A predicate that will match a context with the identifier `StatusBar`
///
/// You can also specify a key-value pair:
///
/// `mode == visible` -> A predicate that will match a context with the key `mode`
/// with the value `visible`
///
/// And a logical operations combining these two checks:
///
/// `StatusBar && mode == visible` -> A predicate that will match a context with the
/// identifier `StatusBar` and the key `mode`
/// with the value `visible`
///
///
/// There is also a special child `>` operator that will match a predicate that is
/// below another predicate:
///
/// `StatusBar > mode == visible` -> A predicate that will match a context identifier `StatusBar`
/// and a child context that has the key `mode` with the
/// value `visible`
///
/// This syntax supports `!=`, `||` and `&&` as logical operators.
/// You can also preface an operation or check with a `!` to negate it.
pub fn parse(source: &str) -> Result<Self> { pub fn parse(source: &str) -> Result<Self> {
let source = skip_whitespace(source); let source = skip_whitespace(source);
let (predicate, rest) = Self::parse_expr(source, 0)?; let (predicate, rest) = Self::parse_expr(source, 0)?;
@ -148,6 +203,7 @@ impl KeyBindingContextPredicate {
} }
} }
/// Eval a predicate against a set of contexts, arranged from lowest to highest.
pub fn eval(&self, contexts: &[KeyContext]) -> bool { pub fn eval(&self, contexts: &[KeyContext]) -> bool {
let Some(context) = contexts.last() else { let Some(context) = contexts.last() else {
return false; return false;

View file

@ -6,9 +6,12 @@ use std::{
collections::HashMap, collections::HashMap,
}; };
/// An opaque identifier of which version of the keymap is currently active.
/// The keymap's version is changed whenever bindings are added or removed.
#[derive(Copy, Clone, Eq, PartialEq, Default)] #[derive(Copy, Clone, Eq, PartialEq, Default)]
pub struct KeymapVersion(usize); pub struct KeymapVersion(usize);
/// A collection of key bindings for the user's application.
#[derive(Default)] #[derive(Default)]
pub struct Keymap { pub struct Keymap {
bindings: Vec<KeyBinding>, bindings: Vec<KeyBinding>,
@ -19,16 +22,19 @@ pub struct Keymap {
} }
impl Keymap { impl Keymap {
/// Create a new keymap with the given bindings.
pub fn new(bindings: Vec<KeyBinding>) -> Self { pub fn new(bindings: Vec<KeyBinding>) -> Self {
let mut this = Self::default(); let mut this = Self::default();
this.add_bindings(bindings); this.add_bindings(bindings);
this this
} }
/// Get the current version of the keymap.
pub fn version(&self) -> KeymapVersion { pub fn version(&self) -> KeymapVersion {
self.version self.version
} }
/// Add more bindings to the keymap.
pub fn add_bindings<T: IntoIterator<Item = KeyBinding>>(&mut self, bindings: T) { pub fn add_bindings<T: IntoIterator<Item = KeyBinding>>(&mut self, bindings: T) {
let no_action_id = (NoAction {}).type_id(); let no_action_id = (NoAction {}).type_id();
@ -51,6 +57,7 @@ impl Keymap {
self.version.0 += 1; self.version.0 += 1;
} }
/// Reset this keymap to its initial state.
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.bindings.clear(); self.bindings.clear();
self.binding_indices_by_action_id.clear(); self.binding_indices_by_action_id.clear();
@ -77,6 +84,7 @@ impl Keymap {
.filter(move |binding| binding.action().partial_eq(action)) .filter(move |binding| binding.action().partial_eq(action))
} }
/// Check if the given binding is enabled, given a certain key context.
pub fn binding_enabled(&self, binding: &KeyBinding, context: &[KeyContext]) -> bool { pub fn binding_enabled(&self, binding: &KeyBinding, context: &[KeyContext]) -> bool {
// If binding has a context predicate, it must match the current context, // If binding has a context predicate, it must match the current context,
if let Some(predicate) = &binding.context_predicate { if let Some(predicate) = &binding.context_predicate {

View file

@ -2,7 +2,7 @@ use crate::{Action, KeyContext, Keymap, KeymapVersion, Keystroke};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::Arc; use std::sync::Arc;
pub struct KeystrokeMatcher { pub(crate) struct KeystrokeMatcher {
pending_keystrokes: Vec<Keystroke>, pending_keystrokes: Vec<Keystroke>,
keymap: Arc<Mutex<Keymap>>, keymap: Arc<Mutex<Keymap>>,
keymap_version: KeymapVersion, keymap_version: KeymapVersion,
@ -35,7 +35,7 @@ impl KeystrokeMatcher {
/// - KeyMatch::Complete(matches) => /// - KeyMatch::Complete(matches) =>
/// One or more bindings have received the necessary key presses. /// One or more bindings have received the necessary key presses.
/// Bindings added later will take precedence over earlier bindings. /// Bindings added later will take precedence over earlier bindings.
pub fn match_keystroke( pub(crate) fn match_keystroke(
&mut self, &mut self,
keystroke: &Keystroke, keystroke: &Keystroke,
context_stack: &[KeyContext], context_stack: &[KeyContext],
@ -85,6 +85,10 @@ impl KeystrokeMatcher {
} }
} }
/// The result of matching a keystroke against a given keybinding.
/// - KeyMatch::None => No match is valid for this key given any pending keystrokes.
/// - KeyMatch::Pending => There exist bindings that is still waiting for more keys.
/// - KeyMatch::Some(matches) => One or more bindings have received the necessary key presses.
#[derive(Debug)] #[derive(Debug)]
pub enum KeyMatch { pub enum KeyMatch {
None, None,
@ -92,19 +96,6 @@ pub enum KeyMatch {
Some(Vec<Box<dyn Action>>), Some(Vec<Box<dyn Action>>),
} }
impl KeyMatch {
pub fn is_some(&self) -> bool {
matches!(self, KeyMatch::Some(_))
}
pub fn matches(self) -> Option<Vec<Box<dyn Action>>> {
match self {
KeyMatch::Some(matches) => Some(matches),
_ => None,
}
}
}
impl PartialEq for KeyMatch { impl PartialEq for KeyMatch {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match (self, other) { match (self, other) {

View file

@ -6,4 +6,4 @@ mod matcher;
pub use binding::*; pub use binding::*;
pub use context::*; pub use context::*;
pub use keymap::*; pub use keymap::*;
pub use matcher::*; pub(crate) use matcher::*;

View file

@ -1,3 +1,7 @@
//! The GPUI prelude is a collection of traits and types that are widely used
//! throughout the library. It is recommended to import this prelude into your
//! application to avoid having to import each trait individually.
pub use crate::{ pub use crate::{
util::FluentBuilder, BorrowAppContext, BorrowWindow, Context, Element, FocusableElement, util::FluentBuilder, BorrowAppContext, BorrowWindow, Context, Element, FocusableElement,
InteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce, InteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce,

View file

@ -10,12 +10,12 @@ pub(crate) type PointF = Point<f32>;
#[allow(non_camel_case_types, unused)] #[allow(non_camel_case_types, unused)]
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>; pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
pub type LayerId = u32; pub(crate) type LayerId = u32;
pub type DrawOrder = u32; pub(crate) type DrawOrder = u32;
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)] #[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[repr(C)] #[repr(C)]
pub struct ViewId { pub(crate) struct ViewId {
low_bits: u32, low_bits: u32,
high_bits: u32, high_bits: u32,
} }
@ -38,7 +38,7 @@ impl From<ViewId> for EntityId {
} }
#[derive(Default)] #[derive(Default)]
pub struct Scene { pub(crate) struct Scene {
layers_by_order: BTreeMap<StackingOrder, LayerId>, layers_by_order: BTreeMap<StackingOrder, LayerId>,
orders_by_layer: BTreeMap<LayerId, StackingOrder>, orders_by_layer: BTreeMap<LayerId, StackingOrder>,
pub(crate) shadows: Vec<Shadow>, pub(crate) shadows: Vec<Shadow>,
@ -429,7 +429,7 @@ impl<'a> Iterator for BatchIterator<'a> {
} }
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)] #[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
pub enum PrimitiveKind { pub(crate) enum PrimitiveKind {
Shadow, Shadow,
#[default] #[default]
Quad, Quad,
@ -495,7 +495,7 @@ pub(crate) enum PrimitiveBatch<'a> {
#[derive(Default, Debug, Clone, Eq, PartialEq)] #[derive(Default, Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub struct Quad { pub(crate) struct Quad {
pub view_id: ViewId, pub view_id: ViewId,
pub layer_id: LayerId, pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
@ -527,7 +527,7 @@ impl From<Quad> for Primitive {
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub struct Underline { pub(crate) struct Underline {
pub view_id: ViewId, pub view_id: ViewId,
pub layer_id: LayerId, pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
@ -558,7 +558,7 @@ impl From<Underline> for Primitive {
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
#[repr(C)] #[repr(C)]
pub struct Shadow { pub(crate) struct Shadow {
pub view_id: ViewId, pub view_id: ViewId,
pub layer_id: LayerId, pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
@ -655,7 +655,7 @@ impl From<PolychromeSprite> for Primitive {
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
pub struct Surface { pub(crate) struct Surface {
pub view_id: ViewId, pub view_id: ViewId,
pub layer_id: LayerId, pub layer_id: LayerId,
pub order: DrawOrder, pub order: DrawOrder,
@ -685,6 +685,7 @@ impl From<Surface> for Primitive {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct PathId(pub(crate) usize); pub(crate) struct PathId(pub(crate) usize);
/// A line made up of a series of vertices and control points.
#[derive(Debug)] #[derive(Debug)]
pub struct Path<P: Clone + Default + Debug> { pub struct Path<P: Clone + Default + Debug> {
pub(crate) id: PathId, pub(crate) id: PathId,
@ -701,6 +702,7 @@ pub struct Path<P: Clone + Default + Debug> {
} }
impl Path<Pixels> { impl Path<Pixels> {
/// Create a new path with the given starting point.
pub fn new(start: Point<Pixels>) -> Self { pub fn new(start: Point<Pixels>) -> Self {
Self { Self {
id: PathId(0), id: PathId(0),
@ -720,6 +722,7 @@ impl Path<Pixels> {
} }
} }
/// Scale this path by the given factor.
pub fn scale(&self, factor: f32) -> Path<ScaledPixels> { pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
Path { Path {
id: self.id, id: self.id,
@ -740,6 +743,7 @@ impl Path<Pixels> {
} }
} }
/// Draw a straight line from the current point to the given point.
pub fn line_to(&mut self, to: Point<Pixels>) { pub fn line_to(&mut self, to: Point<Pixels>) {
self.contour_count += 1; self.contour_count += 1;
if self.contour_count > 1 { if self.contour_count > 1 {
@ -751,6 +755,7 @@ impl Path<Pixels> {
self.current = to; self.current = to;
} }
/// Draw a curve from the current point to the given point, using the given control point.
pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) { pub fn curve_to(&mut self, to: Point<Pixels>, ctrl: Point<Pixels>) {
self.contour_count += 1; self.contour_count += 1;
if self.contour_count > 1 { if self.contour_count > 1 {
@ -833,7 +838,7 @@ impl From<Path<ScaledPixels>> for Primitive {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[repr(C)] #[repr(C)]
pub struct PathVertex<P: Clone + Default + Debug> { pub(crate) struct PathVertex<P: Clone + Default + Debug> {
pub(crate) xy_position: Point<P>, pub(crate) xy_position: Point<P>,
pub(crate) st_position: Point<f32>, pub(crate) st_position: Point<f32>,
pub(crate) content_mask: ContentMask<P>, pub(crate) content_mask: ContentMask<P>,
@ -850,4 +855,4 @@ impl PathVertex<Pixels> {
} }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub struct AtlasId(pub(crate) usize); pub(crate) struct AtlasId(pub(crate) usize);

View file

@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
use std::{borrow::Borrow, sync::Arc}; use std::{borrow::Borrow, sync::Arc};
use util::arc_cow::ArcCow; use util::arc_cow::ArcCow;
/// A shared string is an immutable string that can be cheaply cloned in GPUI
/// tasks. Essentially an abstraction over an Arc<str> and &'static str,
#[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)] #[derive(Deref, DerefMut, Eq, PartialEq, Hash, Clone)]
pub struct SharedString(ArcCow<'static, str>); pub struct SharedString(ArcCow<'static, str>);

View file

@ -147,12 +147,17 @@ where
} }
} }
/// A handle to a subscription created by GPUI. When dropped, the subscription
/// is cancelled and the callback will no longer be invoked.
#[must_use] #[must_use]
pub struct Subscription { pub struct Subscription {
unsubscribe: Option<Box<dyn FnOnce() + 'static>>, unsubscribe: Option<Box<dyn FnOnce() + 'static>>,
} }
impl Subscription { impl Subscription {
/// Detaches the subscription from this handle. The callback will
/// continue to be invoked until the views or models it has been
/// subscribed to are dropped
pub fn detach(mut self) { pub fn detach(mut self) {
self.unsubscribe.take(); self.unsubscribe.take();
} }

View file

@ -3,12 +3,12 @@ use anyhow::anyhow;
use std::{hash::Hash, sync::Arc}; use std::{hash::Hash, sync::Arc};
#[derive(Clone, PartialEq, Hash, Eq)] #[derive(Clone, PartialEq, Hash, Eq)]
pub struct RenderSvgParams { pub(crate) struct RenderSvgParams {
pub(crate) path: SharedString, pub(crate) path: SharedString,
pub(crate) size: Size<DevicePixels>, pub(crate) size: Size<DevicePixels>,
} }
pub struct SvgRenderer { pub(crate) struct SvgRenderer {
asset_source: Arc<dyn AssetSource>, asset_source: Arc<dyn AssetSource>,
} }

View file

@ -229,6 +229,7 @@ impl TaffyLayoutEngine {
} }
} }
/// A unique identifier for a layout node, generated when requesting a layout from Taffy
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Debug)]
#[repr(transparent)] #[repr(transparent)]
pub struct LayoutId(NodeId); pub struct LayoutId(NodeId);
@ -440,6 +441,7 @@ where
} }
} }
/// The space available for an element to be laid out in
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Default, Debug, Eq, PartialEq)]
pub enum AvailableSpace { pub enum AvailableSpace {
/// The amount of space available is the specified number of pixels /// The amount of space available is the specified number of pixels

View file

@ -34,6 +34,9 @@ use std::{
panic::{self, RefUnwindSafe}, panic::{self, RefUnwindSafe},
}; };
/// Run the given test function with the confifured paremeters.
/// This is intended for use with the `gpui::test` macro
/// and generally should not be used directly.
pub fn run_test( pub fn run_test(
mut num_iterations: u64, mut num_iterations: u64,
max_retries: usize, max_retries: usize,
@ -78,6 +81,7 @@ pub fn run_test(
} }
} }
/// A test struct for converting an observation callback into a stream.
pub struct Observation<T> { pub struct Observation<T> {
rx: channel::Receiver<T>, rx: channel::Receiver<T>,
_subscription: Subscription, _subscription: Subscription,

View file

@ -377,7 +377,7 @@ impl TextSystem {
Ok(lines) Ok(lines)
} }
pub fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) { pub(crate) fn finish_frame(&self, reused_views: &FxHashSet<EntityId>) {
self.line_layout_cache.finish_frame(reused_views) self.line_layout_cache.finish_frame(reused_views)
} }

View file

@ -24,6 +24,7 @@ pub struct ShapedLine {
} }
impl ShapedLine { impl ShapedLine {
/// The length of the line in utf-8 bytes.
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.layout.len self.layout.len
} }