Document / lockdown more of GPUI
This commit is contained in:
parent
476de329b3
commit
aa57a4cfbc
27 changed files with 399 additions and 75 deletions
|
@ -8,13 +8,13 @@
|
|||
//! # Element Basics
|
||||
//!
|
||||
//! 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
|
||||
//! 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.
|
||||
//!
|
||||
//! 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
|
||||
//! 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.
|
||||
|
@ -30,7 +30,7 @@
|
|||
//!
|
||||
//! 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
|
||||
//! 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
|
||||
//! your own custom layout algorithm or rendering a code editor.
|
||||
|
||||
|
|
|
@ -9,11 +9,15 @@ use futures::FutureExt;
|
|||
use media::core_video::CVImageBuffer;
|
||||
use util::ResultExt;
|
||||
|
||||
/// A source of image content.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ImageSource {
|
||||
/// Image content will be loaded from provided URI at render time.
|
||||
Uri(SharedUrl),
|
||||
/// Cached image data
|
||||
Data(Arc<ImageData>),
|
||||
// TODO: move surface definitions into mac platform module
|
||||
/// A CoreVideo image buffer
|
||||
Surface(CVImageBuffer),
|
||||
}
|
||||
|
||||
|
@ -47,12 +51,14 @@ impl From<CVImageBuffer> for ImageSource {
|
|||
}
|
||||
}
|
||||
|
||||
/// An image element.
|
||||
pub struct Img {
|
||||
interactivity: Interactivity,
|
||||
source: ImageSource,
|
||||
grayscale: bool,
|
||||
}
|
||||
|
||||
/// Create a new image element.
|
||||
pub fn img(source: impl Into<ImageSource>) -> Img {
|
||||
Img {
|
||||
interactivity: Interactivity::default(),
|
||||
|
@ -62,6 +68,7 @@ pub fn img(source: impl Into<ImageSource>) -> Img {
|
|||
}
|
||||
|
||||
impl Img {
|
||||
/// Set the image to be displayed in grayscale.
|
||||
pub fn grayscale(mut self, grayscale: bool) -> Self {
|
||||
self.grayscale = grayscale;
|
||||
self
|
||||
|
|
|
@ -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::{
|
||||
point, px, AnyElement, AvailableSpace, BorrowAppContext, BorrowWindow, Bounds, ContentMask,
|
||||
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 sum_tree::{Bias, SumTree};
|
||||
|
||||
/// Construct a new list element
|
||||
pub fn list(state: ListState) -> List {
|
||||
List {
|
||||
state,
|
||||
|
@ -15,11 +24,13 @@ pub fn list(state: ListState) -> List {
|
|||
}
|
||||
}
|
||||
|
||||
/// A list element
|
||||
pub struct List {
|
||||
state: ListState,
|
||||
style: StyleRefinement,
|
||||
}
|
||||
|
||||
/// The list state that views must hold on behalf of the list element.
|
||||
#[derive(Clone)]
|
||||
pub struct ListState(Rc<RefCell<StateInner>>);
|
||||
|
||||
|
@ -35,15 +46,24 @@ struct StateInner {
|
|||
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)]
|
||||
pub enum ListAlignment {
|
||||
/// The list is scrolling from top to bottom, like most lists.
|
||||
Top,
|
||||
/// The list is scrolling from bottom to top, like a chat log.
|
||||
Bottom,
|
||||
}
|
||||
|
||||
/// A scroll event that has been converted to be in terms of the list's items.
|
||||
pub struct ListScrollEvent {
|
||||
/// The range of items currently visible in the list, after applying the scroll event.
|
||||
pub visible_range: Range<usize>,
|
||||
|
||||
/// The number of items that are currently visible in the list, after applying the scroll event.
|
||||
pub count: usize,
|
||||
|
||||
/// Whether the list has been scrolled.
|
||||
pub is_scrolled: bool,
|
||||
}
|
||||
|
||||
|
@ -74,6 +94,11 @@ struct UnrenderedCount(usize);
|
|||
struct Height(Pixels);
|
||||
|
||||
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>(
|
||||
element_count: usize,
|
||||
orientation: ListAlignment,
|
||||
|
@ -111,10 +136,13 @@ impl ListState {
|
|||
.extend((0..element_count).map(|_| ListItem::Unrendered), &());
|
||||
}
|
||||
|
||||
/// The number of items in this list.
|
||||
pub fn item_count(&self) -> usize {
|
||||
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) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
|
||||
|
@ -141,6 +169,7 @@ impl ListState {
|
|||
state.items = new_heights;
|
||||
}
|
||||
|
||||
/// Set a handler that will be called when the list is scrolled.
|
||||
pub fn set_scroll_handler(
|
||||
&self,
|
||||
handler: impl FnMut(&ListScrollEvent, &mut WindowContext) + 'static,
|
||||
|
@ -148,10 +177,12 @@ impl ListState {
|
|||
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 {
|
||||
self.0.borrow().logical_scroll_top()
|
||||
}
|
||||
|
||||
/// Scroll the list to the given offset
|
||||
pub fn scroll_to(&self, mut scroll_top: ListOffset) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
let item_count = state.items.summary().count;
|
||||
|
@ -163,6 +194,7 @@ impl ListState {
|
|||
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) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
|
||||
|
@ -193,7 +225,8 @@ impl ListState {
|
|||
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>> {
|
||||
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)]
|
||||
pub struct ListOffset {
|
||||
/// The index of an item in the list
|
||||
pub item_ix: usize,
|
||||
/// The number of pixels to offset from the item index.
|
||||
pub offset_in_item: Pixels,
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,13 @@ use crate::{
|
|||
Point, Size, Style, WindowContext,
|
||||
};
|
||||
|
||||
/// The state that the overlay element uses to track its children.
|
||||
pub struct OverlayState {
|
||||
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 {
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
anchor_corner: AnchorCorner,
|
||||
|
@ -191,15 +194,21 @@ enum Axis {
|
|||
Vertical,
|
||||
}
|
||||
|
||||
/// Which algorithm to use when fitting the overlay to be inside the window.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum OverlayFitMode {
|
||||
/// Snap the overlay to the window edge
|
||||
SnapToWindow,
|
||||
/// Switch which corner anchor this overlay is attached to
|
||||
SwitchAnchor,
|
||||
}
|
||||
|
||||
/// Which algorithm to use when positioning the overlay.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub enum OverlayPositionMode {
|
||||
/// Position the overlay relative to the window
|
||||
Window,
|
||||
/// Position the overlay relative to its parent
|
||||
Local,
|
||||
}
|
||||
|
||||
|
@ -226,11 +235,16 @@ impl OverlayPositionMode {
|
|||
}
|
||||
}
|
||||
|
||||
/// Which corner of the overlay should be considered the anchor.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum AnchorCorner {
|
||||
/// The top left corner
|
||||
TopLeft,
|
||||
/// The top right corner
|
||||
TopRight,
|
||||
/// The bottom left corner
|
||||
BottomLeft,
|
||||
/// The bottom right corner
|
||||
BottomRight,
|
||||
}
|
||||
|
||||
|
@ -255,6 +269,7 @@ impl AnchorCorner {
|
|||
Bounds { origin, size }
|
||||
}
|
||||
|
||||
/// Get the point corresponding to this anchor corner in `bounds`.
|
||||
pub fn corner(&self, bounds: Bounds<Pixels>) -> Point<Pixels> {
|
||||
match self {
|
||||
Self::TopLeft => bounds.origin,
|
||||
|
|
|
@ -4,11 +4,13 @@ use crate::{
|
|||
};
|
||||
use util::ResultExt;
|
||||
|
||||
/// An SVG element.
|
||||
pub struct Svg {
|
||||
interactivity: Interactivity,
|
||||
path: Option<SharedString>,
|
||||
}
|
||||
|
||||
/// Create a new SVG element.
|
||||
pub fn svg() -> Svg {
|
||||
Svg {
|
||||
interactivity: Interactivity::default(),
|
||||
|
@ -17,6 +19,7 @@ pub fn svg() -> Svg {
|
|||
}
|
||||
|
||||
impl Svg {
|
||||
/// Set the path to the SVG file for this element.
|
||||
pub fn path(mut self, path: impl Into<SharedString>) -> Self {
|
||||
self.path = Some(path.into());
|
||||
self
|
||||
|
|
|
@ -87,6 +87,7 @@ pub struct StyledText {
|
|||
}
|
||||
|
||||
impl StyledText {
|
||||
/// Construct a new styled text element from the given string.
|
||||
pub fn new(text: impl Into<SharedString>) -> Self {
|
||||
StyledText {
|
||||
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(
|
||||
mut self,
|
||||
default_style: &TextStyle,
|
||||
|
@ -151,6 +154,7 @@ impl IntoElement for StyledText {
|
|||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Default, Clone)]
|
||||
pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
|
||||
|
||||
|
@ -290,6 +294,7 @@ impl TextState {
|
|||
}
|
||||
}
|
||||
|
||||
/// A text element that can be interacted with.
|
||||
pub struct InteractiveText {
|
||||
element_id: ElementId,
|
||||
text: StyledText,
|
||||
|
@ -305,6 +310,7 @@ struct InteractiveTextClickEvent {
|
|||
mouse_up_index: usize,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct InteractiveTextState {
|
||||
text_state: TextState,
|
||||
mouse_down_index: Rc<Cell<Option<usize>>>,
|
||||
|
@ -314,6 +320,7 @@ pub struct InteractiveTextState {
|
|||
|
||||
/// InteractiveTest is a wrapper around StyledText that adds mouse interactions.
|
||||
impl InteractiveText {
|
||||
/// Creates a new InteractiveText from the given text.
|
||||
pub fn new(id: impl Into<ElementId>, text: StyledText) -> Self {
|
||||
Self {
|
||||
element_id: id.into(),
|
||||
|
|
|
@ -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::{
|
||||
point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, ContentMask, Element,
|
||||
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 {
|
||||
id: ElementId,
|
||||
item_count: usize,
|
||||
|
@ -63,18 +70,22 @@ pub struct UniformList {
|
|||
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)]
|
||||
pub struct UniformListScrollHandle {
|
||||
deferred_scroll_to_item: Rc<RefCell<Option<usize>>>,
|
||||
}
|
||||
|
||||
impl UniformListScrollHandle {
|
||||
/// Create a new scroll handle to bind to a uniform list.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
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) {
|
||||
self.deferred_scroll_to_item.replace(Some(ix));
|
||||
}
|
||||
|
@ -86,6 +97,7 @@ impl Styled for UniformList {
|
|||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Default)]
|
||||
pub struct UniformListState {
|
||||
interactive: InteractiveElementState,
|
||||
|
@ -262,6 +274,7 @@ impl IntoElement for UniformList {
|
|||
}
|
||||
|
||||
impl UniformList {
|
||||
/// Selects a specific list item for measurement.
|
||||
pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
|
||||
self.item_to_measure_index = item_index.unwrap_or(0);
|
||||
self
|
||||
|
@ -284,6 +297,7 @@ impl UniformList {
|
|||
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 {
|
||||
self.scroll_handle = Some(handle);
|
||||
self
|
||||
|
|
|
@ -21,11 +21,15 @@ use waker_fn::waker_fn;
|
|||
#[cfg(any(test, feature = "test-support"))]
|
||||
use rand::rngs::StdRng;
|
||||
|
||||
/// A pointer to the executor that is currently running,
|
||||
/// for spawning background tasks.
|
||||
#[derive(Clone)]
|
||||
pub struct BackgroundExecutor {
|
||||
dispatcher: Arc<dyn PlatformDispatcher>,
|
||||
}
|
||||
|
||||
/// A pointer to the executor that is currently running,
|
||||
/// for spawning tasks on the main thread.
|
||||
#[derive(Clone)]
|
||||
pub struct ForegroundExecutor {
|
||||
dispatcher: Arc<dyn PlatformDispatcher>,
|
||||
|
@ -37,11 +41,14 @@ pub struct ForegroundExecutor {
|
|||
/// It implements [`Future`] so you can `.await` on it.
|
||||
///
|
||||
/// 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]
|
||||
#[derive(Debug)]
|
||||
pub enum Task<T> {
|
||||
/// A task that is ready to return a value
|
||||
Ready(Option<T>),
|
||||
|
||||
/// A task that is currently running.
|
||||
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)]
|
||||
pub struct TaskLabel(NonZeroUsize);
|
||||
|
||||
|
@ -97,6 +106,7 @@ impl Default for TaskLabel {
|
|||
}
|
||||
|
||||
impl TaskLabel {
|
||||
/// Construct a new task label.
|
||||
pub fn new() -> Self {
|
||||
static NEXT_TASK_LABEL: AtomicUsize = AtomicUsize::new(1);
|
||||
Self(NEXT_TASK_LABEL.fetch_add(1, SeqCst).try_into().unwrap())
|
||||
|
@ -363,6 +373,7 @@ impl BackgroundExecutor {
|
|||
|
||||
/// ForegroundExecutor runs things on the main thread.
|
||||
impl ForegroundExecutor {
|
||||
/// Creates a new ForegroundExecutor from the given PlatformDispatcher.
|
||||
pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
|
||||
Self {
|
||||
dispatcher,
|
||||
|
@ -411,13 +422,14 @@ impl<'a> Scope<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Spawn a future into this scope.
|
||||
pub fn spawn<F>(&mut self, f: F)
|
||||
where
|
||||
F: Future<Output = ()> + Send + 'a,
|
||||
{
|
||||
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.
|
||||
let f = unsafe {
|
||||
mem::transmute::<
|
||||
|
|
|
@ -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]
|
||||
mod action;
|
||||
mod app;
|
||||
|
@ -58,10 +88,10 @@ pub use elements::*;
|
|||
pub use executor::*;
|
||||
pub use geometry::*;
|
||||
pub use gpui_macros::{register_action, test, IntoElement, Render};
|
||||
pub use image_cache::*;
|
||||
use image_cache::*;
|
||||
pub use input::*;
|
||||
pub use interactive::*;
|
||||
pub use key_dispatch::*;
|
||||
use key_dispatch::*;
|
||||
pub use keymap::*;
|
||||
pub use platform::*;
|
||||
pub use refineable::*;
|
||||
|
@ -73,7 +103,7 @@ pub use smol::Timer;
|
|||
pub use style::*;
|
||||
pub use styled::*;
|
||||
pub use subscription::*;
|
||||
pub use svg_renderer::*;
|
||||
use svg_renderer::*;
|
||||
pub use taffy::{AvailableSpace, LayoutId};
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use test::*;
|
||||
|
@ -82,20 +112,23 @@ pub use util::arc_cow::ArcCow;
|
|||
pub use view::*;
|
||||
pub use window::*;
|
||||
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::BorrowMut,
|
||||
};
|
||||
use std::{any::Any, borrow::BorrowMut};
|
||||
use taffy::TaffyLayoutEngine;
|
||||
|
||||
/// The context trait, allows the different contexts in GPUI to be used
|
||||
/// interchangeably for certain operations.
|
||||
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>;
|
||||
|
||||
/// Create a new model in the app context.
|
||||
fn new_model<T: 'static>(
|
||||
&mut self,
|
||||
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
|
||||
) -> Self::Result<Model<T>>;
|
||||
|
||||
/// Update a model in the app context.
|
||||
fn update_model<T, R>(
|
||||
&mut self,
|
||||
handle: &Model<T>,
|
||||
|
@ -104,6 +137,7 @@ pub trait Context {
|
|||
where
|
||||
T: 'static;
|
||||
|
||||
/// Read a model from the app context.
|
||||
fn read_model<T, R>(
|
||||
&self,
|
||||
handle: &Model<T>,
|
||||
|
@ -112,10 +146,12 @@ pub trait Context {
|
|||
where
|
||||
T: 'static;
|
||||
|
||||
/// Update a window for the given handle.
|
||||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T;
|
||||
|
||||
/// Read a window off of the application context.
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
|
@ -125,7 +161,10 @@ pub trait Context {
|
|||
T: 'static;
|
||||
}
|
||||
|
||||
/// This trait is used for the different visual contexts in GPUI that
|
||||
/// require a window to be present.
|
||||
pub trait VisualContext: Context {
|
||||
/// Construct a new view in the window referenced by this context.
|
||||
fn new_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
|
@ -133,12 +172,14 @@ pub trait VisualContext: Context {
|
|||
where
|
||||
V: 'static + Render;
|
||||
|
||||
/// Update a view with the given callback
|
||||
fn update_view<V: 'static, R>(
|
||||
&mut self,
|
||||
view: &View<V>,
|
||||
update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
|
||||
) -> Self::Result<R>;
|
||||
|
||||
/// Replace the root view of a window with a new view.
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
|
@ -146,38 +187,47 @@ pub trait VisualContext: Context {
|
|||
where
|
||||
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<()>
|
||||
where
|
||||
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<()>
|
||||
where
|
||||
V: ManagedView;
|
||||
}
|
||||
|
||||
/// A trait that allows models and views to be interchangeable in certain operations
|
||||
pub trait Entity<T>: Sealed {
|
||||
/// The weak reference type for this entity.
|
||||
type Weak: 'static;
|
||||
|
||||
/// The ID for this entity
|
||||
fn entity_id(&self) -> EntityId;
|
||||
|
||||
/// Downgrade this entity to a weak reference.
|
||||
fn downgrade(&self) -> Self::Weak;
|
||||
|
||||
/// Upgrade this entity from a weak reference.
|
||||
fn upgrade_from(weak: &Self::Weak) -> Option<Self>
|
||||
where
|
||||
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 enum GlobalKey {
|
||||
Numeric(usize),
|
||||
View(EntityId),
|
||||
Type(TypeId),
|
||||
}
|
||||
|
||||
/// A helper trait for auto-implementing certain methods on contexts that
|
||||
/// can be used interchangeably.
|
||||
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
|
||||
where
|
||||
F: FnOnce(&mut Self) -> R;
|
||||
|
||||
/// Set a global value on the context.
|
||||
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> {
|
||||
/// Convert this type into a simple `Result<T>`.
|
||||
fn flatten(self) -> Result<T>;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,12 +11,12 @@ use thiserror::Error;
|
|||
use util::http::{self, HttpClient};
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
pub struct RenderImageParams {
|
||||
pub(crate) struct RenderImageParams {
|
||||
pub(crate) image_id: ImageId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, Clone)]
|
||||
pub enum Error {
|
||||
pub(crate) enum Error {
|
||||
#[error("http error: {0}")]
|
||||
Client(#[from] http::Error),
|
||||
#[error("IO error: {0}")]
|
||||
|
@ -42,7 +42,7 @@ impl From<ImageError> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ImageCache {
|
||||
pub(crate) struct ImageCache {
|
||||
client: Arc<dyn HttpClient>,
|
||||
images: Arc<Mutex<HashMap<SharedUrl, FetchImageFuture>>>,
|
||||
}
|
||||
|
|
|
@ -3,20 +3,33 @@ use std::ops::Range;
|
|||
|
||||
/// 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`].
|
||||
///
|
||||
/// See [`InputHandler`] for details on how to implement each method.
|
||||
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>)
|
||||
-> Option<String>;
|
||||
|
||||
/// See [`InputHandler::selected_text_range`] for details
|
||||
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>>;
|
||||
|
||||
/// See [`InputHandler::unmark_text`] for details
|
||||
fn unmark_text(&mut self, cx: &mut ViewContext<Self>);
|
||||
|
||||
/// See [`InputHandler::replace_text_in_range`] for details
|
||||
fn replace_text_in_range(
|
||||
&mut self,
|
||||
range: Option<Range<usize>>,
|
||||
text: &str,
|
||||
cx: &mut ViewContext<Self>,
|
||||
);
|
||||
|
||||
/// See [`InputHandler::replace_and_mark_text_in_range`] for details
|
||||
fn replace_and_mark_text_in_range(
|
||||
&mut self,
|
||||
range: Option<Range<usize>>,
|
||||
|
@ -24,6 +37,8 @@ pub trait ViewInputHandler: 'static + Sized {
|
|||
new_selected_range: Option<Range<usize>>,
|
||||
cx: &mut ViewContext<Self>,
|
||||
);
|
||||
|
||||
/// See [`InputHandler::bounds_for_range`] for details
|
||||
fn bounds_for_range(
|
||||
&mut self,
|
||||
range_utf16: Range<usize>,
|
||||
|
|
|
@ -4,15 +4,25 @@ use crate::{
|
|||
use smallvec::SmallVec;
|
||||
use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
|
||||
|
||||
/// An event from a platform input source.
|
||||
pub trait InputEvent: Sealed + 'static {
|
||||
/// Convert this event into the platform input enum.
|
||||
fn to_platform_input(self) -> PlatformInput;
|
||||
}
|
||||
|
||||
/// A key event from the platform.
|
||||
pub trait KeyEvent: InputEvent {}
|
||||
|
||||
/// A mouse event from the platform.
|
||||
pub trait MouseEvent: InputEvent {}
|
||||
|
||||
/// The key down event equivalent for the platform.
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct KeyDownEvent {
|
||||
/// The keystroke that was generated.
|
||||
pub keystroke: Keystroke,
|
||||
|
||||
/// Whether the key is currently held down.
|
||||
pub is_held: bool,
|
||||
}
|
||||
|
||||
|
@ -24,8 +34,10 @@ impl InputEvent for KeyDownEvent {
|
|||
}
|
||||
impl KeyEvent for KeyDownEvent {}
|
||||
|
||||
/// The key up event equivalent for the platform.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KeyUpEvent {
|
||||
/// The keystroke that was released.
|
||||
pub keystroke: Keystroke,
|
||||
}
|
||||
|
||||
|
@ -37,8 +49,10 @@ impl InputEvent for KeyUpEvent {
|
|||
}
|
||||
impl KeyEvent for KeyUpEvent {}
|
||||
|
||||
/// The modifiers changed event equivalent for the platform.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ModifiersChangedEvent {
|
||||
/// The new state of the modifier keys
|
||||
pub modifiers: Modifiers,
|
||||
}
|
||||
|
||||
|
@ -62,17 +76,28 @@ impl Deref for ModifiersChangedEvent {
|
|||
/// Based on the winit enum of the same name.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub enum TouchPhase {
|
||||
/// The touch started.
|
||||
Started,
|
||||
/// The touch event is moving.
|
||||
#[default]
|
||||
Moved,
|
||||
/// The touch phase has ended
|
||||
Ended,
|
||||
}
|
||||
|
||||
/// A mouse down event from the platform
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MouseDownEvent {
|
||||
/// Which mouse button was pressed.
|
||||
pub button: MouseButton,
|
||||
|
||||
/// The position of the mouse on the window.
|
||||
pub position: Point<Pixels>,
|
||||
|
||||
/// The modifiers that were held down when the mouse was pressed.
|
||||
pub modifiers: Modifiers,
|
||||
|
||||
/// The number of times the button has been clicked.
|
||||
pub click_count: usize,
|
||||
}
|
||||
|
||||
|
@ -84,11 +109,19 @@ impl InputEvent for MouseDownEvent {
|
|||
}
|
||||
impl MouseEvent for MouseDownEvent {}
|
||||
|
||||
/// A mouse up event from the platform
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MouseUpEvent {
|
||||
/// Which mouse button was released.
|
||||
pub button: MouseButton,
|
||||
|
||||
/// The position of the mouse on the window.
|
||||
pub position: Point<Pixels>,
|
||||
|
||||
/// The modifiers that were held down when the mouse was released.
|
||||
pub modifiers: Modifiers,
|
||||
|
||||
/// The number of times the button has been clicked.
|
||||
pub click_count: usize,
|
||||
}
|
||||
|
||||
|
@ -100,21 +133,34 @@ impl InputEvent for MouseUpEvent {
|
|||
}
|
||||
impl MouseEvent for MouseUpEvent {}
|
||||
|
||||
/// A click event, generated when a mouse button is pressed and released.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ClickEvent {
|
||||
/// The mouse event when the button was pressed.
|
||||
pub down: MouseDownEvent,
|
||||
|
||||
/// The mouse event when the button was released.
|
||||
pub up: MouseUpEvent,
|
||||
}
|
||||
|
||||
/// An enum representing the mouse button that was pressed.
|
||||
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum MouseButton {
|
||||
/// The left mouse button.
|
||||
Left,
|
||||
|
||||
/// The right mouse button.
|
||||
Right,
|
||||
|
||||
/// The middle mouse button.
|
||||
Middle,
|
||||
|
||||
/// A navigation button, such as back or forward.
|
||||
Navigate(NavigationDirection),
|
||||
}
|
||||
|
||||
impl MouseButton {
|
||||
/// Get all the mouse buttons in a list.
|
||||
pub fn all() -> Vec<Self> {
|
||||
vec![
|
||||
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)]
|
||||
pub enum NavigationDirection {
|
||||
/// The back button.
|
||||
Back,
|
||||
|
||||
/// The forward button.
|
||||
Forward,
|
||||
}
|
||||
|
||||
|
@ -144,10 +194,16 @@ impl Default for NavigationDirection {
|
|||
}
|
||||
}
|
||||
|
||||
/// A mouse move event from the platform
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MouseMoveEvent {
|
||||
/// The position of the mouse on the window.
|
||||
pub position: Point<Pixels>,
|
||||
|
||||
/// The mouse button that was pressed, if any.
|
||||
pub pressed_button: Option<MouseButton>,
|
||||
|
||||
/// The modifiers that were held down when the mouse was moved.
|
||||
pub modifiers: Modifiers,
|
||||
}
|
||||
|
||||
|
@ -160,16 +216,25 @@ impl InputEvent for MouseMoveEvent {
|
|||
impl MouseEvent for MouseMoveEvent {}
|
||||
|
||||
impl MouseMoveEvent {
|
||||
/// Returns true if the left mouse button is currently held down.
|
||||
pub fn dragging(&self) -> bool {
|
||||
self.pressed_button == Some(MouseButton::Left)
|
||||
}
|
||||
}
|
||||
|
||||
/// A mouse wheel event from the platform
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ScrollWheelEvent {
|
||||
/// The position of the mouse on the window.
|
||||
pub position: Point<Pixels>,
|
||||
|
||||
/// The change in scroll wheel position for this event.
|
||||
pub delta: ScrollDelta,
|
||||
|
||||
/// The modifiers that were held down when the mouse was moved.
|
||||
pub modifiers: Modifiers,
|
||||
|
||||
/// The phase of the touch event.
|
||||
pub touch_phase: TouchPhase,
|
||||
}
|
||||
|
||||
|
@ -189,9 +254,12 @@ impl Deref for ScrollWheelEvent {
|
|||
}
|
||||
}
|
||||
|
||||
/// The scroll delta for a scroll wheel event.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum ScrollDelta {
|
||||
/// An exact scroll delta in pixels.
|
||||
Pixels(Point<Pixels>),
|
||||
/// An inexact scroll delta in lines.
|
||||
Lines(Point<f32>),
|
||||
}
|
||||
|
||||
|
@ -202,6 +270,7 @@ impl Default for ScrollDelta {
|
|||
}
|
||||
|
||||
impl ScrollDelta {
|
||||
/// Returns true if this is a precise scroll delta in pixels.
|
||||
pub fn precise(&self) -> bool {
|
||||
match self {
|
||||
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> {
|
||||
match self {
|
||||
ScrollDelta::Pixels(delta) => *delta,
|
||||
|
@ -216,6 +286,7 @@ impl ScrollDelta {
|
|||
}
|
||||
}
|
||||
|
||||
/// Combines two scroll deltas into one.
|
||||
pub fn coalesce(self, other: ScrollDelta) -> ScrollDelta {
|
||||
match (self, other) {
|
||||
(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)]
|
||||
pub struct MouseExitEvent {
|
||||
/// The position of the mouse relative to the window.
|
||||
pub position: Point<Pixels>,
|
||||
/// The mouse button that was pressed, if any.
|
||||
pub pressed_button: Option<MouseButton>,
|
||||
/// The modifiers that were held down when the mouse was moved.
|
||||
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)]
|
||||
pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
|
||||
|
||||
impl ExternalPaths {
|
||||
/// Convert this collection of paths into a slice.
|
||||
pub fn paths(&self) -> &[PathBuf] {
|
||||
&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)]
|
||||
pub enum FileDropEvent {
|
||||
/// The files have entered the window.
|
||||
Entered {
|
||||
/// The position of the mouse relative to the window.
|
||||
position: Point<Pixels>,
|
||||
/// The paths of the files that are being dragged.
|
||||
paths: ExternalPaths,
|
||||
},
|
||||
/// The files are being dragged over the window
|
||||
Pending {
|
||||
/// The position of the mouse relative to the window.
|
||||
position: Point<Pixels>,
|
||||
},
|
||||
/// The files have been dropped onto the window.
|
||||
Submit {
|
||||
/// The position of the mouse relative to the window.
|
||||
position: Point<Pixels>,
|
||||
},
|
||||
/// The user has stopped dragging the files over the window.
|
||||
Exited,
|
||||
}
|
||||
|
||||
|
@ -292,40 +379,31 @@ impl InputEvent for FileDropEvent {
|
|||
}
|
||||
impl MouseEvent for FileDropEvent {}
|
||||
|
||||
/// An enum corresponding to all kinds of platform input events.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum PlatformInput {
|
||||
/// A key was pressed.
|
||||
KeyDown(KeyDownEvent),
|
||||
/// A key was released.
|
||||
KeyUp(KeyUpEvent),
|
||||
/// The keyboard modifiers were changed.
|
||||
ModifiersChanged(ModifiersChangedEvent),
|
||||
/// The mouse was pressed.
|
||||
MouseDown(MouseDownEvent),
|
||||
/// The mouse was released.
|
||||
MouseUp(MouseUpEvent),
|
||||
/// The mouse was moved.
|
||||
MouseMove(MouseMoveEvent),
|
||||
/// The mouse exited the window.
|
||||
MouseExited(MouseExitEvent),
|
||||
/// The scroll wheel was used.
|
||||
ScrollWheel(ScrollWheelEvent),
|
||||
/// Files were dragged and dropped onto the window.
|
||||
FileDrop(FileDropEvent),
|
||||
}
|
||||
|
||||
impl PlatformInput {
|
||||
pub fn position(&self) -> Option<Point<Pixels>> {
|
||||
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> {
|
||||
pub(crate) fn mouse_event(&self) -> Option<&dyn Any> {
|
||||
match self {
|
||||
PlatformInput::KeyDown { .. } => 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 {
|
||||
PlatformInput::KeyDown(event) => Some(event),
|
||||
PlatformInput::KeyUp(event) => Some(event),
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::{
|
|||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
pub struct DispatchNodeId(usize);
|
||||
pub(crate) struct DispatchNodeId(usize);
|
||||
|
||||
pub(crate) struct DispatchTree {
|
||||
node_stack: Vec<DispatchNodeId>,
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::{Action, KeyBindingContextPredicate, KeyMatch, Keystroke};
|
|||
use anyhow::Result;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// A keybinding and it's associated metadata, from the keymap.
|
||||
pub struct KeyBinding {
|
||||
pub(crate) action: Box<dyn Action>,
|
||||
pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
|
||||
|
@ -19,10 +20,12 @@ impl Clone for 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 {
|
||||
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> {
|
||||
let context = if let Some(context) = 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 {
|
||||
if self.keystrokes.as_ref().starts_with(pending_keystrokes) {
|
||||
// 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] {
|
||||
self.keystrokes.as_slice()
|
||||
}
|
||||
|
||||
/// Get the action associated with this binding
|
||||
pub fn action(&self) -> &dyn Action {
|
||||
self.action.as_ref()
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@ use anyhow::{anyhow, Result};
|
|||
use smallvec::SmallVec;
|
||||
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)]
|
||||
pub struct KeyContext(SmallVec<[ContextEntry; 1]>);
|
||||
|
||||
|
@ -21,6 +25,11 @@ impl<'a> TryFrom<&'a str> for 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> {
|
||||
let mut context = Self::default();
|
||||
let source = skip_whitespace(source);
|
||||
|
@ -53,14 +62,17 @@ impl KeyContext {
|
|||
Self::parse_expr(source, context)
|
||||
}
|
||||
|
||||
/// Check if this context is empty.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
/// Clear this context.
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear();
|
||||
}
|
||||
|
||||
/// Extend this context with another context.
|
||||
pub fn extend(&mut self, other: &Self) {
|
||||
for entry in &other.0 {
|
||||
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) {
|
||||
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) {
|
||||
let key = key.into();
|
||||
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 {
|
||||
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> {
|
||||
self.0
|
||||
.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)]
|
||||
pub enum KeyBindingContextPredicate {
|
||||
/// A predicate that will match a given identifier.
|
||||
Identifier(SharedString),
|
||||
/// A predicate that will match a given key-value pair.
|
||||
Equal(SharedString, SharedString),
|
||||
/// A predicate that will match a given key-value pair not being present.
|
||||
NotEqual(SharedString, SharedString),
|
||||
/// A predicate that will match a given predicate appearing below another predicate.
|
||||
/// in the element tree
|
||||
Child(
|
||||
Box<KeyBindingContextPredicate>,
|
||||
Box<KeyBindingContextPredicate>,
|
||||
),
|
||||
/// Predicate that will invert another predicate.
|
||||
Not(Box<KeyBindingContextPredicate>),
|
||||
/// A predicate that will match if both of its children match.
|
||||
And(
|
||||
Box<KeyBindingContextPredicate>,
|
||||
Box<KeyBindingContextPredicate>,
|
||||
),
|
||||
/// A predicate that will match if either of its children match.
|
||||
Or(
|
||||
Box<KeyBindingContextPredicate>,
|
||||
Box<KeyBindingContextPredicate>,
|
||||
|
@ -138,6 +165,34 @@ pub enum 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> {
|
||||
let source = skip_whitespace(source);
|
||||
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 {
|
||||
let Some(context) = contexts.last() else {
|
||||
return false;
|
||||
|
|
|
@ -6,9 +6,12 @@ use std::{
|
|||
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)]
|
||||
pub struct KeymapVersion(usize);
|
||||
|
||||
/// A collection of key bindings for the user's application.
|
||||
#[derive(Default)]
|
||||
pub struct Keymap {
|
||||
bindings: Vec<KeyBinding>,
|
||||
|
@ -19,16 +22,19 @@ pub struct Keymap {
|
|||
}
|
||||
|
||||
impl Keymap {
|
||||
/// Create a new keymap with the given bindings.
|
||||
pub fn new(bindings: Vec<KeyBinding>) -> Self {
|
||||
let mut this = Self::default();
|
||||
this.add_bindings(bindings);
|
||||
this
|
||||
}
|
||||
|
||||
/// Get the current version of the keymap.
|
||||
pub fn version(&self) -> KeymapVersion {
|
||||
self.version
|
||||
}
|
||||
|
||||
/// Add more bindings to the keymap.
|
||||
pub fn add_bindings<T: IntoIterator<Item = KeyBinding>>(&mut self, bindings: T) {
|
||||
let no_action_id = (NoAction {}).type_id();
|
||||
|
||||
|
@ -51,6 +57,7 @@ impl Keymap {
|
|||
self.version.0 += 1;
|
||||
}
|
||||
|
||||
/// Reset this keymap to its initial state.
|
||||
pub fn clear(&mut self) {
|
||||
self.bindings.clear();
|
||||
self.binding_indices_by_action_id.clear();
|
||||
|
@ -77,6 +84,7 @@ impl Keymap {
|
|||
.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 {
|
||||
// If binding has a context predicate, it must match the current context,
|
||||
if let Some(predicate) = &binding.context_predicate {
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::{Action, KeyContext, Keymap, KeymapVersion, Keystroke};
|
|||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct KeystrokeMatcher {
|
||||
pub(crate) struct KeystrokeMatcher {
|
||||
pending_keystrokes: Vec<Keystroke>,
|
||||
keymap: Arc<Mutex<Keymap>>,
|
||||
keymap_version: KeymapVersion,
|
||||
|
@ -35,7 +35,7 @@ impl KeystrokeMatcher {
|
|||
/// - KeyMatch::Complete(matches) =>
|
||||
/// One or more bindings have received the necessary key presses.
|
||||
/// Bindings added later will take precedence over earlier bindings.
|
||||
pub fn match_keystroke(
|
||||
pub(crate) fn match_keystroke(
|
||||
&mut self,
|
||||
keystroke: &Keystroke,
|
||||
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)]
|
||||
pub enum KeyMatch {
|
||||
None,
|
||||
|
@ -92,19 +96,6 @@ pub enum KeyMatch {
|
|||
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 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
|
|
|
@ -6,4 +6,4 @@ mod matcher;
|
|||
pub use binding::*;
|
||||
pub use context::*;
|
||||
pub use keymap::*;
|
||||
pub use matcher::*;
|
||||
pub(crate) use matcher::*;
|
||||
|
|
|
@ -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::{
|
||||
util::FluentBuilder, BorrowAppContext, BorrowWindow, Context, Element, FocusableElement,
|
||||
InteractiveElement, IntoElement, ParentElement, Refineable, Render, RenderOnce,
|
||||
|
|
|
@ -10,12 +10,12 @@ pub(crate) type PointF = Point<f32>;
|
|||
#[allow(non_camel_case_types, unused)]
|
||||
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
|
||||
|
||||
pub type LayerId = u32;
|
||||
pub type DrawOrder = u32;
|
||||
pub(crate) type LayerId = u32;
|
||||
pub(crate) type DrawOrder = u32;
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[repr(C)]
|
||||
pub struct ViewId {
|
||||
pub(crate) struct ViewId {
|
||||
low_bits: u32,
|
||||
high_bits: u32,
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ impl From<ViewId> for EntityId {
|
|||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Scene {
|
||||
pub(crate) struct Scene {
|
||||
layers_by_order: BTreeMap<StackingOrder, LayerId>,
|
||||
orders_by_layer: BTreeMap<LayerId, StackingOrder>,
|
||||
pub(crate) shadows: Vec<Shadow>,
|
||||
|
@ -429,7 +429,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd, Default)]
|
||||
pub enum PrimitiveKind {
|
||||
pub(crate) enum PrimitiveKind {
|
||||
Shadow,
|
||||
#[default]
|
||||
Quad,
|
||||
|
@ -495,7 +495,7 @@ pub(crate) enum PrimitiveBatch<'a> {
|
|||
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Quad {
|
||||
pub(crate) struct Quad {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
|
@ -527,7 +527,7 @@ impl From<Quad> for Primitive {
|
|||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Underline {
|
||||
pub(crate) struct Underline {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
|
@ -558,7 +558,7 @@ impl From<Underline> for Primitive {
|
|||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct Shadow {
|
||||
pub(crate) struct Shadow {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
|
@ -655,7 +655,7 @@ impl From<PolychromeSprite> for Primitive {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Surface {
|
||||
pub(crate) struct Surface {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
|
@ -685,6 +685,7 @@ impl From<Surface> for Primitive {
|
|||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct PathId(pub(crate) usize);
|
||||
|
||||
/// A line made up of a series of vertices and control points.
|
||||
#[derive(Debug)]
|
||||
pub struct Path<P: Clone + Default + Debug> {
|
||||
pub(crate) id: PathId,
|
||||
|
@ -701,6 +702,7 @@ pub struct Path<P: Clone + Default + Debug> {
|
|||
}
|
||||
|
||||
impl Path<Pixels> {
|
||||
/// Create a new path with the given starting point.
|
||||
pub fn new(start: Point<Pixels>) -> Self {
|
||||
Self {
|
||||
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> {
|
||||
Path {
|
||||
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>) {
|
||||
self.contour_count += 1;
|
||||
if self.contour_count > 1 {
|
||||
|
@ -751,6 +755,7 @@ impl Path<Pixels> {
|
|||
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>) {
|
||||
self.contour_count += 1;
|
||||
if self.contour_count > 1 {
|
||||
|
@ -833,7 +838,7 @@ impl From<Path<ScaledPixels>> for Primitive {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
#[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) st_position: Point<f32>,
|
||||
pub(crate) content_mask: ContentMask<P>,
|
||||
|
@ -850,4 +855,4 @@ impl PathVertex<Pixels> {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct AtlasId(pub(crate) usize);
|
||||
pub(crate) struct AtlasId(pub(crate) usize);
|
||||
|
|
|
@ -3,6 +3,8 @@ use serde::{Deserialize, Serialize};
|
|||
use std::{borrow::Borrow, sync::Arc};
|
||||
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)]
|
||||
pub struct SharedString(ArcCow<'static, str>);
|
||||
|
||||
|
|
|
@ -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]
|
||||
pub struct Subscription {
|
||||
unsubscribe: Option<Box<dyn FnOnce() + 'static>>,
|
||||
}
|
||||
|
||||
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) {
|
||||
self.unsubscribe.take();
|
||||
}
|
||||
|
|
|
@ -3,12 +3,12 @@ use anyhow::anyhow;
|
|||
use std::{hash::Hash, sync::Arc};
|
||||
|
||||
#[derive(Clone, PartialEq, Hash, Eq)]
|
||||
pub struct RenderSvgParams {
|
||||
pub(crate) struct RenderSvgParams {
|
||||
pub(crate) path: SharedString,
|
||||
pub(crate) size: Size<DevicePixels>,
|
||||
}
|
||||
|
||||
pub struct SvgRenderer {
|
||||
pub(crate) struct SvgRenderer {
|
||||
asset_source: Arc<dyn AssetSource>,
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]
|
||||
#[repr(transparent)]
|
||||
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)]
|
||||
pub enum AvailableSpace {
|
||||
/// The amount of space available is the specified number of pixels
|
||||
|
|
|
@ -34,6 +34,9 @@ use std::{
|
|||
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(
|
||||
mut num_iterations: u64,
|
||||
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> {
|
||||
rx: channel::Receiver<T>,
|
||||
_subscription: Subscription,
|
||||
|
|
|
@ -377,7 +377,7 @@ impl TextSystem {
|
|||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ pub struct ShapedLine {
|
|||
}
|
||||
|
||||
impl ShapedLine {
|
||||
/// The length of the line in utf-8 bytes.
|
||||
pub fn len(&self) -> usize {
|
||||
self.layout.len
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue