Merge branch 'main' into cursors
This commit is contained in:
+76
-5
@@ -1,3 +1,5 @@
|
||||
#![deny(missing_docs)]
|
||||
|
||||
mod async_context;
|
||||
mod entity_map;
|
||||
mod model_context;
|
||||
@@ -43,6 +45,9 @@ use util::{
|
||||
ResultExt,
|
||||
};
|
||||
|
||||
/// The duration for which futures returned from [AppContext::on_app_context] or [ModelContext::on_app_quit] can run before the application fully quits.
|
||||
pub const SHUTDOWN_TIMEOUT: Duration = Duration::from_millis(100);
|
||||
|
||||
/// Temporary(?) wrapper around [`RefCell<AppContext>`] to help us debug any double borrows.
|
||||
/// Strongly consider removing after stabilization.
|
||||
#[doc(hidden)]
|
||||
@@ -106,14 +111,24 @@ pub struct App(Rc<AppCell>);
|
||||
/// configured, you'll start the app with `App::run`.
|
||||
impl App {
|
||||
/// Builds an app with the given asset source.
|
||||
pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self(AppContext::new(
|
||||
current_platform(),
|
||||
asset_source,
|
||||
Arc::new(()),
|
||||
http::client(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Assign
|
||||
pub fn with_assets(self, asset_source: impl AssetSource) -> Self {
|
||||
let mut context_lock = self.0.borrow_mut();
|
||||
let asset_source = Arc::new(asset_source);
|
||||
context_lock.asset_source = asset_source.clone();
|
||||
context_lock.svg_renderer = SvgRenderer::new(asset_source);
|
||||
drop(context_lock);
|
||||
self
|
||||
}
|
||||
|
||||
/// Start the application. The provided callback will be called once the
|
||||
/// app is fully launched.
|
||||
pub fn run<F>(self, on_finish_launching: F)
|
||||
@@ -187,6 +202,9 @@ type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()
|
||||
type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
|
||||
type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
|
||||
|
||||
/// Contains the state of the full application, and passed as a reference to a variety of callbacks.
|
||||
/// Other contexts such as [ModelContext], [WindowContext], and [ViewContext] deref to this type, making it the most general context type.
|
||||
/// You need a reference to an `AppContext` to access the state of a [Model].
|
||||
pub struct AppContext {
|
||||
pub(crate) this: Weak<AppCell>,
|
||||
pub(crate) platform: Rc<dyn Platform>,
|
||||
@@ -312,7 +330,7 @@ impl AppContext {
|
||||
let futures = futures::future::join_all(futures);
|
||||
if self
|
||||
.background_executor
|
||||
.block_with_timeout(Duration::from_millis(100), futures)
|
||||
.block_with_timeout(SHUTDOWN_TIMEOUT, futures)
|
||||
.is_err()
|
||||
{
|
||||
log::error!("timed out waiting on app_will_quit");
|
||||
@@ -446,6 +464,7 @@ impl AppContext {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Returns a handle to the window that is currently focused at the platform level, if one exists.
|
||||
pub fn active_window(&self) -> Option<AnyWindowHandle> {
|
||||
self.platform.active_window()
|
||||
}
|
||||
@@ -474,14 +493,17 @@ impl AppContext {
|
||||
self.platform.activate(ignoring_other_apps);
|
||||
}
|
||||
|
||||
/// Hide the application at the platform level.
|
||||
pub fn hide(&self) {
|
||||
self.platform.hide();
|
||||
}
|
||||
|
||||
/// Hide other applications at the platform level.
|
||||
pub fn hide_other_apps(&self) {
|
||||
self.platform.hide_other_apps();
|
||||
}
|
||||
|
||||
/// Unhide other applications at the platform level.
|
||||
pub fn unhide_other_apps(&self) {
|
||||
self.platform.unhide_other_apps();
|
||||
}
|
||||
@@ -521,18 +543,25 @@ impl AppContext {
|
||||
self.platform.open_url(url);
|
||||
}
|
||||
|
||||
/// Returns the full pathname of the current app bundle.
|
||||
/// If the app is not being run from a bundle, returns an error.
|
||||
pub fn app_path(&self) -> Result<PathBuf> {
|
||||
self.platform.app_path()
|
||||
}
|
||||
|
||||
/// Returns the file URL of the executable with the specified name in the application bundle
|
||||
pub fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
|
||||
self.platform.path_for_auxiliary_executable(name)
|
||||
}
|
||||
|
||||
/// Returns the maximum duration in which a second mouse click must occur for an event to be a double-click event.
|
||||
pub fn double_click_interval(&self) -> Duration {
|
||||
self.platform.double_click_interval()
|
||||
}
|
||||
|
||||
/// Displays a platform modal for selecting paths.
|
||||
/// When one or more paths are selected, they'll be relayed asynchronously via the returned oneshot channel.
|
||||
/// If cancelled, a `None` will be relayed instead.
|
||||
pub fn prompt_for_paths(
|
||||
&self,
|
||||
options: PathPromptOptions,
|
||||
@@ -540,22 +569,30 @@ impl AppContext {
|
||||
self.platform.prompt_for_paths(options)
|
||||
}
|
||||
|
||||
/// Displays a platform modal for selecting a new path where a file can be saved.
|
||||
/// The provided directory will be used to set the iniital location.
|
||||
/// When a path is selected, it is relayed asynchronously via the returned oneshot channel.
|
||||
/// If cancelled, a `None` will be relayed instead.
|
||||
pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver<Option<PathBuf>> {
|
||||
self.platform.prompt_for_new_path(directory)
|
||||
}
|
||||
|
||||
/// Reveals the specified path at the platform level, such as in Finder on macOS.
|
||||
pub fn reveal_path(&self, path: &Path) {
|
||||
self.platform.reveal_path(path)
|
||||
}
|
||||
|
||||
/// Returns whether the user has configured scrollbars to auto-hide at the platform level.
|
||||
pub fn should_auto_hide_scrollbars(&self) -> bool {
|
||||
self.platform.should_auto_hide_scrollbars()
|
||||
}
|
||||
|
||||
/// Restart the application.
|
||||
pub fn restart(&self) {
|
||||
self.platform.restart()
|
||||
}
|
||||
|
||||
/// Returns the local timezone at the platform level.
|
||||
pub fn local_timezone(&self) -> UtcOffset {
|
||||
self.platform.local_timezone()
|
||||
}
|
||||
@@ -745,7 +782,7 @@ impl AppContext {
|
||||
}
|
||||
|
||||
/// Spawns the future returned by the given function on the thread pool. The closure will be invoked
|
||||
/// with AsyncAppContext, which allows the application state to be accessed across await points.
|
||||
/// with [AsyncAppContext], which allows the application state to be accessed across await points.
|
||||
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
|
||||
where
|
||||
Fut: Future<Output = R> + 'static,
|
||||
@@ -896,6 +933,8 @@ impl AppContext {
|
||||
self.globals_by_type.insert(global_type, lease.global);
|
||||
}
|
||||
|
||||
/// Arrange for the given function to be invoked whenever a view of the specified type is created.
|
||||
/// The function will be passed a mutable reference to the view along with an appropriate context.
|
||||
pub fn observe_new_views<V: 'static>(
|
||||
&mut self,
|
||||
on_new: impl 'static + Fn(&mut V, &mut ViewContext<V>),
|
||||
@@ -915,6 +954,8 @@ impl AppContext {
|
||||
subscription
|
||||
}
|
||||
|
||||
/// Observe the release of a model or view. The callback is invoked after the model or view
|
||||
/// has no more strong references but before it has been dropped.
|
||||
pub fn observe_release<E, T>(
|
||||
&mut self,
|
||||
handle: &E,
|
||||
@@ -935,6 +976,9 @@ impl AppContext {
|
||||
subscription
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when a keystroke is received by the application
|
||||
/// in any window. Note that this fires after all other action and event mechanisms have resolved
|
||||
/// and that this API will not be invoked if the event's propagation is stopped.
|
||||
pub fn observe_keystrokes(
|
||||
&mut self,
|
||||
f: impl FnMut(&KeystrokeEvent, &mut WindowContext) + 'static,
|
||||
@@ -958,6 +1002,7 @@ impl AppContext {
|
||||
self.pending_effects.push_back(Effect::Refresh);
|
||||
}
|
||||
|
||||
/// Clear all key bindings in the app.
|
||||
pub fn clear_key_bindings(&mut self) {
|
||||
self.keymap.lock().clear();
|
||||
self.pending_effects.push_back(Effect::Refresh);
|
||||
@@ -992,6 +1037,7 @@ impl AppContext {
|
||||
self.propagate_event = true;
|
||||
}
|
||||
|
||||
/// Build an action from some arbitrary data, typically a keymap entry.
|
||||
pub fn build_action(
|
||||
&self,
|
||||
name: &str,
|
||||
@@ -1000,10 +1046,16 @@ impl AppContext {
|
||||
self.actions.build_action(name, data)
|
||||
}
|
||||
|
||||
/// Get a list of all action names that have been registered.
|
||||
/// in the application. Note that registration only allows for
|
||||
/// actions to be built dynamically, and is unrelated to binding
|
||||
/// actions in the element tree.
|
||||
pub fn all_action_names(&self) -> &[SharedString] {
|
||||
self.actions.all_action_names()
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when the application is about to quit.
|
||||
/// It is not possible to cancel the quit event at this point.
|
||||
pub fn on_app_quit<Fut>(
|
||||
&mut self,
|
||||
mut on_quit: impl FnMut(&mut AppContext) -> Fut + 'static,
|
||||
@@ -1039,6 +1091,8 @@ impl AppContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given action is bound in the current context, as defined by the app's current focus,
|
||||
/// the bindings in the element tree, and any global action listeners.
|
||||
pub fn is_action_available(&mut self, action: &dyn Action) -> bool {
|
||||
if let Some(window) = self.active_window() {
|
||||
if let Ok(window_action_available) =
|
||||
@@ -1052,10 +1106,13 @@ impl AppContext {
|
||||
.contains_key(&action.as_any().type_id())
|
||||
}
|
||||
|
||||
/// Set the menu bar for this application. This will replace any existing menu bar.
|
||||
pub fn set_menus(&mut self, menus: Vec<Menu>) {
|
||||
self.platform.set_menus(menus, &self.keymap.lock());
|
||||
}
|
||||
|
||||
/// Dispatch an action to the currently active window or global action handler
|
||||
/// See [action::Action] for more information on how actions work
|
||||
pub fn dispatch_action(&mut self, action: &dyn Action) {
|
||||
if let Some(active_window) = self.active_window() {
|
||||
active_window
|
||||
@@ -1110,6 +1167,7 @@ impl AppContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// Is there currently something being dragged?
|
||||
pub fn has_active_drag(&self) -> bool {
|
||||
self.active_drag.is_some()
|
||||
}
|
||||
@@ -1119,7 +1177,7 @@ impl Context for AppContext {
|
||||
type Result<T> = T;
|
||||
|
||||
/// Build an entity that is owned by the application. The given function will be invoked with
|
||||
/// a `ModelContext` and must return an object representing the entity. A `Model` will be returned
|
||||
/// a `ModelContext` and must return an object representing the entity. A `Model` handle will be returned,
|
||||
/// which can be used to access the entity in a context.
|
||||
fn new_model<T: 'static>(
|
||||
&mut self,
|
||||
@@ -1262,8 +1320,14 @@ impl<G: 'static> DerefMut for GlobalLease<G> {
|
||||
/// Contains state associated with an active drag operation, started by dragging an element
|
||||
/// within the window or by dragging into the app from the underlying platform.
|
||||
pub struct AnyDrag {
|
||||
/// The view used to render this drag
|
||||
pub view: AnyView,
|
||||
|
||||
/// The value of the dragged item, to be dropped
|
||||
pub value: Box<dyn Any>,
|
||||
|
||||
/// This is used to render the dragged item in the same place
|
||||
/// on the original element that the drag was initiated
|
||||
pub cursor_offset: Point<Pixels>,
|
||||
}
|
||||
|
||||
@@ -1271,12 +1335,19 @@ pub struct AnyDrag {
|
||||
/// tooltip behavior on a custom element. Otherwise, use [Div::tooltip].
|
||||
#[derive(Clone)]
|
||||
pub struct AnyTooltip {
|
||||
/// The view used to display the tooltip
|
||||
pub view: AnyView,
|
||||
|
||||
/// The offset from the cursor to use, relative to the parent view
|
||||
pub cursor_offset: Point<Pixels>,
|
||||
}
|
||||
|
||||
/// A keystroke event, and potentially the associated action
|
||||
#[derive(Debug)]
|
||||
pub struct KeystrokeEvent {
|
||||
/// The keystroke that occurred
|
||||
pub keystroke: Keystroke,
|
||||
|
||||
/// The action that was resolved for the keystroke, if any
|
||||
pub action: Option<Box<dyn Action>>,
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ use anyhow::{anyhow, Context as _};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use std::{future::Future, rc::Weak};
|
||||
|
||||
/// An async-friendly version of [AppContext] with a static lifetime so it can be held across `await` points in async code.
|
||||
/// You're provided with an instance when calling [AppContext::spawn], and you can also create one with [AppContext::to_async].
|
||||
/// Internally, this holds a weak reference to an `AppContext`, so its methods are fallible to protect against cases where the [AppContext] is dropped.
|
||||
#[derive(Clone)]
|
||||
pub struct AsyncAppContext {
|
||||
pub(crate) app: Weak<AppCell>,
|
||||
@@ -139,6 +142,8 @@ impl AsyncAppContext {
|
||||
self.foreground_executor.spawn(f(self.clone()))
|
||||
}
|
||||
|
||||
/// Determine whether global state of the specified type has been assigned.
|
||||
/// Returns an error if the `AppContext` has been dropped.
|
||||
pub fn has_global<G: 'static>(&self) -> Result<bool> {
|
||||
let app = self
|
||||
.app
|
||||
@@ -148,6 +153,9 @@ impl AsyncAppContext {
|
||||
Ok(app.has_global::<G>())
|
||||
}
|
||||
|
||||
/// Reads the global state of the specified type, passing it to the given callback.
|
||||
/// Panics if no global state of the specified type has been assigned.
|
||||
/// Returns an error if the `AppContext` has been dropped.
|
||||
pub fn read_global<G: 'static, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> Result<R> {
|
||||
let app = self
|
||||
.app
|
||||
@@ -157,6 +165,9 @@ impl AsyncAppContext {
|
||||
Ok(read(app.global(), &app))
|
||||
}
|
||||
|
||||
/// Reads the global state of the specified type, passing it to the given callback.
|
||||
/// Similar to [read_global], but returns an error instead of panicking if no state of the specified type has been assigned.
|
||||
/// Returns an error if no state of the specified type has been assigned the `AppContext` has been dropped.
|
||||
pub fn try_read_global<G: 'static, R>(
|
||||
&self,
|
||||
read: impl FnOnce(&G, &AppContext) -> R,
|
||||
@@ -166,6 +177,8 @@ impl AsyncAppContext {
|
||||
Some(read(app.try_global()?, &app))
|
||||
}
|
||||
|
||||
/// A convenience method for [AppContext::update_global]
|
||||
/// for updating the global state of the specified type.
|
||||
pub fn update_global<G: 'static, R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(&mut G, &mut AppContext) -> R,
|
||||
@@ -179,6 +192,8 @@ impl AsyncAppContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// A cloneable, owned handle to the application context,
|
||||
/// composed with the window associated with the current task.
|
||||
#[derive(Clone, Deref, DerefMut)]
|
||||
pub struct AsyncWindowContext {
|
||||
#[deref]
|
||||
@@ -188,14 +203,16 @@ pub struct AsyncWindowContext {
|
||||
}
|
||||
|
||||
impl AsyncWindowContext {
|
||||
pub fn window_handle(&self) -> AnyWindowHandle {
|
||||
self.window
|
||||
}
|
||||
|
||||
pub(crate) fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
|
||||
Self { app, window }
|
||||
}
|
||||
|
||||
/// Get the handle of the window this context is associated with.
|
||||
pub fn window_handle(&self) -> AnyWindowHandle {
|
||||
self.window
|
||||
}
|
||||
|
||||
/// A convenience method for [WindowContext::update()]
|
||||
pub fn update<R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(AnyView, &mut WindowContext) -> R,
|
||||
@@ -203,10 +220,12 @@ impl AsyncWindowContext {
|
||||
self.app.update_window(self.window, update)
|
||||
}
|
||||
|
||||
/// A convenience method for [WindowContext::on_next_frame()]
|
||||
pub fn on_next_frame(&mut self, f: impl FnOnce(&mut WindowContext) + 'static) {
|
||||
self.window.update(self, |_, cx| cx.on_next_frame(f)).ok();
|
||||
}
|
||||
|
||||
/// A convenience method for [AppContext::global()]
|
||||
pub fn read_global<G: 'static, R>(
|
||||
&mut self,
|
||||
read: impl FnOnce(&G, &WindowContext) -> R,
|
||||
@@ -214,6 +233,8 @@ impl AsyncWindowContext {
|
||||
self.window.update(self, |_, cx| read(cx.global(), cx))
|
||||
}
|
||||
|
||||
/// A convenience method for [AppContext::update_global()]
|
||||
/// for updating the global state of the specified type.
|
||||
pub fn update_global<G, R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(&mut G, &mut WindowContext) -> R,
|
||||
@@ -224,6 +245,8 @@ impl AsyncWindowContext {
|
||||
self.window.update(self, |_, cx| cx.update_global(update))
|
||||
}
|
||||
|
||||
/// Schedule a future to be executed on the main thread. This is used for collecting
|
||||
/// the results of background tasks and updating the UI.
|
||||
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncWindowContext) -> Fut) -> Task<R>
|
||||
where
|
||||
Fut: Future<Output = R> + 'static,
|
||||
|
||||
@@ -31,6 +31,7 @@ impl From<u64> for EntityId {
|
||||
}
|
||||
|
||||
impl EntityId {
|
||||
/// Converts this entity id to a [u64]
|
||||
pub fn as_u64(self) -> u64 {
|
||||
self.0.as_ffi()
|
||||
}
|
||||
@@ -140,7 +141,7 @@ impl EntityMap {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lease<'a, T> {
|
||||
pub(crate) struct Lease<'a, T> {
|
||||
entity: Option<Box<dyn Any>>,
|
||||
pub model: &'a Model<T>,
|
||||
entity_type: PhantomData<T>,
|
||||
@@ -169,8 +170,9 @@ impl<'a, T> Drop for Lease<'a, T> {
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct Slot<T>(Model<T>);
|
||||
pub(crate) struct Slot<T>(Model<T>);
|
||||
|
||||
/// A dynamically typed reference to a model, which can be downcast into a `Model<T>`.
|
||||
pub struct AnyModel {
|
||||
pub(crate) entity_id: EntityId,
|
||||
pub(crate) entity_type: TypeId,
|
||||
@@ -195,14 +197,17 @@ impl AnyModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the id associated with this model.
|
||||
pub fn entity_id(&self) -> EntityId {
|
||||
self.entity_id
|
||||
}
|
||||
|
||||
/// Returns the [TypeId] associated with this model.
|
||||
pub fn entity_type(&self) -> TypeId {
|
||||
self.entity_type
|
||||
}
|
||||
|
||||
/// Converts this model handle into a weak variant, which does not prevent it from being released.
|
||||
pub fn downgrade(&self) -> AnyWeakModel {
|
||||
AnyWeakModel {
|
||||
entity_id: self.entity_id,
|
||||
@@ -211,6 +216,8 @@ impl AnyModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this model handle into a strongly-typed model handle of the given type.
|
||||
/// If this model handle is not of the specified type, returns itself as an error variant.
|
||||
pub fn downcast<T: 'static>(self) -> Result<Model<T>, AnyModel> {
|
||||
if TypeId::of::<T>() == self.entity_type {
|
||||
Ok(Model {
|
||||
@@ -274,7 +281,7 @@ impl Drop for AnyModel {
|
||||
entity_map
|
||||
.write()
|
||||
.leak_detector
|
||||
.handle_dropped(self.entity_id, self.handle_id)
|
||||
.handle_released(self.entity_id, self.handle_id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -307,6 +314,8 @@ impl std::fmt::Debug for AnyModel {
|
||||
}
|
||||
}
|
||||
|
||||
/// A strong, well typed reference to a struct which is managed
|
||||
/// by GPUI
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct Model<T> {
|
||||
#[deref]
|
||||
@@ -368,10 +377,12 @@ impl<T: 'static> Model<T> {
|
||||
self.any_model
|
||||
}
|
||||
|
||||
/// Grab a reference to this entity from the context.
|
||||
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
|
||||
cx.entities.read(self)
|
||||
}
|
||||
|
||||
/// Read the entity referenced by this model with the given function.
|
||||
pub fn read_with<R, C: Context>(
|
||||
&self,
|
||||
cx: &C,
|
||||
@@ -437,6 +448,7 @@ impl<T> PartialEq<WeakModel<T>> for Model<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A type erased, weak reference to a model.
|
||||
#[derive(Clone)]
|
||||
pub struct AnyWeakModel {
|
||||
pub(crate) entity_id: EntityId,
|
||||
@@ -445,10 +457,12 @@ pub struct AnyWeakModel {
|
||||
}
|
||||
|
||||
impl AnyWeakModel {
|
||||
/// Get the entity ID associated with this weak reference.
|
||||
pub fn entity_id(&self) -> EntityId {
|
||||
self.entity_id
|
||||
}
|
||||
|
||||
/// Check if this weak handle can be upgraded, or if the model has already been dropped
|
||||
pub fn is_upgradable(&self) -> bool {
|
||||
let ref_count = self
|
||||
.entity_ref_counts
|
||||
@@ -458,6 +472,7 @@ impl AnyWeakModel {
|
||||
ref_count > 0
|
||||
}
|
||||
|
||||
/// Upgrade this weak model reference to a strong reference.
|
||||
pub fn upgrade(&self) -> Option<AnyModel> {
|
||||
let ref_counts = &self.entity_ref_counts.upgrade()?;
|
||||
let ref_counts = ref_counts.read();
|
||||
@@ -485,14 +500,15 @@ impl AnyWeakModel {
|
||||
})
|
||||
}
|
||||
|
||||
/// Assert that model referenced by this weak handle has been released.
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn assert_dropped(&self) {
|
||||
pub fn assert_released(&self) {
|
||||
self.entity_ref_counts
|
||||
.upgrade()
|
||||
.unwrap()
|
||||
.write()
|
||||
.leak_detector
|
||||
.assert_dropped(self.entity_id);
|
||||
.assert_released(self.entity_id);
|
||||
|
||||
if self
|
||||
.entity_ref_counts
|
||||
@@ -527,6 +543,7 @@ impl PartialEq for AnyWeakModel {
|
||||
|
||||
impl Eq for AnyWeakModel {}
|
||||
|
||||
/// A weak reference to a model of the given type.
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct WeakModel<T> {
|
||||
#[deref]
|
||||
@@ -617,12 +634,12 @@ lazy_static::lazy_static! {
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
|
||||
pub struct HandleId {
|
||||
pub(crate) struct HandleId {
|
||||
id: u64, // id of the handle itself, not the pointed at object
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub struct LeakDetector {
|
||||
pub(crate) struct LeakDetector {
|
||||
next_handle_id: u64,
|
||||
entity_handles: HashMap<EntityId, HashMap<HandleId, Option<backtrace::Backtrace>>>,
|
||||
}
|
||||
@@ -641,12 +658,12 @@ impl LeakDetector {
|
||||
handle_id
|
||||
}
|
||||
|
||||
pub fn handle_dropped(&mut self, entity_id: EntityId, handle_id: HandleId) {
|
||||
pub fn handle_released(&mut self, entity_id: EntityId, handle_id: HandleId) {
|
||||
let handles = self.entity_handles.entry(entity_id).or_default();
|
||||
handles.remove(&handle_id);
|
||||
}
|
||||
|
||||
pub fn assert_dropped(&mut self, entity_id: EntityId) {
|
||||
pub fn assert_released(&mut self, entity_id: EntityId) {
|
||||
let handles = self.entity_handles.entry(entity_id).or_default();
|
||||
if !handles.is_empty() {
|
||||
for (_, backtrace) in handles {
|
||||
|
||||
@@ -11,6 +11,7 @@ use std::{
|
||||
future::Future,
|
||||
};
|
||||
|
||||
/// The app context, with specialized behavior for the given model.
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct ModelContext<'a, T> {
|
||||
#[deref]
|
||||
@@ -24,20 +25,24 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
Self { app, model_state }
|
||||
}
|
||||
|
||||
/// The entity id of the model backing this context.
|
||||
pub fn entity_id(&self) -> EntityId {
|
||||
self.model_state.entity_id
|
||||
}
|
||||
|
||||
/// Returns a handle to the model belonging to this context.
|
||||
pub fn handle(&self) -> Model<T> {
|
||||
self.weak_model()
|
||||
.upgrade()
|
||||
.expect("The entity must be alive if we have a model context")
|
||||
}
|
||||
|
||||
/// Returns a weak handle to the model belonging to this context.
|
||||
pub fn weak_model(&self) -> WeakModel<T> {
|
||||
self.model_state.clone()
|
||||
}
|
||||
|
||||
/// Arranges for the given function to be called whenever [ModelContext::notify] or [ViewContext::notify] is called with the given model or view.
|
||||
pub fn observe<W, E>(
|
||||
&mut self,
|
||||
entity: &E,
|
||||
@@ -59,6 +64,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Subscribe to an event type from another model or view
|
||||
pub fn subscribe<T2, E, Evt>(
|
||||
&mut self,
|
||||
entity: &E,
|
||||
@@ -81,6 +87,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when GPUI releases this model.
|
||||
pub fn on_release(
|
||||
&mut self,
|
||||
on_release: impl FnOnce(&mut T, &mut AppContext) + 'static,
|
||||
@@ -99,6 +106,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
subscription
|
||||
}
|
||||
|
||||
/// Register a callback to be run on the release of another model or view
|
||||
pub fn observe_release<T2, E>(
|
||||
&mut self,
|
||||
entity: &E,
|
||||
@@ -124,6 +132,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
subscription
|
||||
}
|
||||
|
||||
/// Register a callback to for updates to the given global
|
||||
pub fn observe_global<G: 'static>(
|
||||
&mut self,
|
||||
mut f: impl FnMut(&mut T, &mut ModelContext<'_, T>) + 'static,
|
||||
@@ -140,6 +149,8 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
subscription
|
||||
}
|
||||
|
||||
/// Arrange for the given function to be invoked whenever the application is quit.
|
||||
/// The future returned from this callback will be polled for up to [gpui::SHUTDOWN_TIMEOUT] until the app fully quits.
|
||||
pub fn on_app_quit<Fut>(
|
||||
&mut self,
|
||||
mut on_quit: impl FnMut(&mut T, &mut ModelContext<T>) -> Fut + 'static,
|
||||
@@ -165,6 +176,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
subscription
|
||||
}
|
||||
|
||||
/// Tell GPUI that this model has changed and observers of it should be notified.
|
||||
pub fn notify(&mut self) {
|
||||
if self
|
||||
.app
|
||||
@@ -177,6 +189,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the given global
|
||||
pub fn update_global<G, R>(&mut self, f: impl FnOnce(&mut G, &mut Self) -> R) -> R
|
||||
where
|
||||
G: 'static,
|
||||
@@ -187,6 +200,9 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
result
|
||||
}
|
||||
|
||||
/// Spawn the future returned by the given function.
|
||||
/// The function is provided a weak handle to the model owned by this context and a context that can be held across await points.
|
||||
/// The returned task must be held or detached.
|
||||
pub fn spawn<Fut, R>(&self, f: impl FnOnce(WeakModel<T>, AsyncAppContext) -> Fut) -> Task<R>
|
||||
where
|
||||
T: 'static,
|
||||
@@ -199,6 +215,7 @@ impl<'a, T: 'static> ModelContext<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T> ModelContext<'a, T> {
|
||||
/// Emit an event of the specified type, which can be handled by other entities that have subscribed via `subscribe` methods on their respective contexts.
|
||||
pub fn emit<Evt>(&mut self, event: Evt)
|
||||
where
|
||||
T: EventEmitter<Evt>,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use crate::{
|
||||
div, Action, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
|
||||
BackgroundExecutor, ClipboardItem, Context, Entity, EventEmitter, ForegroundExecutor,
|
||||
IntoElement, Keystroke, Model, ModelContext, Pixels, Platform, Render, Result, Size, Task,
|
||||
TestDispatcher, TestPlatform, TestWindow, TextSystem, View, ViewContext, VisualContext,
|
||||
WindowContext, WindowHandle, WindowOptions,
|
||||
Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
|
||||
AvailableSpace, BackgroundExecutor, Bounds, ClipboardItem, Context, Entity, EventEmitter,
|
||||
ForegroundExecutor, InputEvent, Keystroke, Model, ModelContext, Pixels, Platform, Point,
|
||||
Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow, TextSystem, View,
|
||||
ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
use futures::{Stream, StreamExt};
|
||||
@@ -167,10 +167,14 @@ impl TestAppContext {
|
||||
}
|
||||
|
||||
/// Adds a new window with no content.
|
||||
pub fn add_empty_window(&mut self) -> AnyWindowHandle {
|
||||
pub fn add_empty_window(&mut self) -> &mut VisualTestContext {
|
||||
let mut cx = self.app.borrow_mut();
|
||||
cx.open_window(WindowOptions::default(), |cx| cx.new_view(|_| EmptyView {}))
|
||||
.any_handle
|
||||
let window = cx.open_window(WindowOptions::default(), |cx| cx.new_view(|_| ()));
|
||||
drop(cx);
|
||||
let cx = Box::new(VisualTestContext::from_window(*window.deref(), self));
|
||||
cx.run_until_parked();
|
||||
// it might be nice to try and cleanup these at the end of each test.
|
||||
Box::leak(cx)
|
||||
}
|
||||
|
||||
/// Adds a new window, and returns its root view and a `VisualTestContext` which can be used
|
||||
@@ -564,6 +568,11 @@ pub struct VisualTestContext {
|
||||
}
|
||||
|
||||
impl<'a> VisualTestContext {
|
||||
/// Get the underlying window handle underlying this context.
|
||||
pub fn handle(&self) -> AnyWindowHandle {
|
||||
self.window
|
||||
}
|
||||
|
||||
/// Provides the `WindowContext` for the duration of the closure.
|
||||
pub fn update<R>(&mut self, f: impl FnOnce(&mut WindowContext) -> R) -> R {
|
||||
self.cx.update_window(self.window, |_, cx| f(cx)).unwrap()
|
||||
@@ -609,6 +618,46 @@ impl<'a> VisualTestContext {
|
||||
self.cx.simulate_input(self.window, input)
|
||||
}
|
||||
|
||||
/// Simulates the user resizing the window to the new size.
|
||||
pub fn simulate_resize(&self, size: Size<Pixels>) {
|
||||
self.simulate_window_resize(self.window, size)
|
||||
}
|
||||
|
||||
/// debug_bounds returns the bounds of the element with the given selector.
|
||||
pub fn debug_bounds(&mut self, selector: &'static str) -> Option<Bounds<Pixels>> {
|
||||
self.update(|cx| cx.window.rendered_frame.debug_bounds.get(selector).copied())
|
||||
}
|
||||
|
||||
/// Draw an element to the window. Useful for simulating events or actions
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
origin: Point<Pixels>,
|
||||
space: Size<AvailableSpace>,
|
||||
f: impl FnOnce(&mut WindowContext) -> AnyElement,
|
||||
) {
|
||||
self.update(|cx| {
|
||||
let entity_id = cx
|
||||
.window
|
||||
.root_view
|
||||
.as_ref()
|
||||
.expect("Can't draw to this window without a root view")
|
||||
.entity_id();
|
||||
cx.with_view_id(entity_id, |cx| {
|
||||
f(cx).draw(origin, space, cx);
|
||||
});
|
||||
|
||||
cx.refresh();
|
||||
})
|
||||
}
|
||||
|
||||
/// Simulate an event from the platform, e.g. a SrollWheelEvent
|
||||
/// Make sure you've called [VisualTestContext::draw] first!
|
||||
pub fn simulate_event<E: InputEvent>(&mut self, event: E) {
|
||||
self.test_window(self.window)
|
||||
.simulate_input(event.to_platform_input());
|
||||
self.background_executor.run_until_parked();
|
||||
}
|
||||
|
||||
/// Simulates the user blurring the window.
|
||||
pub fn deactivate_window(&mut self) {
|
||||
if Some(self.window) == self.test_platform.active_window() {
|
||||
@@ -763,12 +812,3 @@ impl AnyWindowHandle {
|
||||
self.update(cx, |_, cx| cx.new_view(build_view)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// An EmptyView for testing.
|
||||
pub struct EmptyView {}
|
||||
|
||||
impl Render for EmptyView {
|
||||
fn render(&mut self, _cx: &mut crate::ViewContext<Self>) -> impl IntoElement {
|
||||
div()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,6 +115,12 @@ pub trait Render: 'static + Sized {
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement;
|
||||
}
|
||||
|
||||
impl Render for () {
|
||||
fn render(&mut self, _cx: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
/// You can derive [`IntoElement`] on any type that implements this trait.
|
||||
/// It is used to allow views to be expressed in terms of abstract data.
|
||||
pub trait RenderOnce: 'static {
|
||||
|
||||
@@ -416,6 +416,18 @@ pub trait InteractiveElement: Sized {
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
fn debug_selector(mut self, f: impl FnOnce() -> String) -> Self {
|
||||
self.interactivity().debug_selector = Some(f());
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(not(any(test, feature = "test-support")))]
|
||||
#[inline]
|
||||
fn debug_selector(self, _: impl FnOnce() -> String) -> Self {
|
||||
self
|
||||
}
|
||||
|
||||
fn capture_any_mouse_down(
|
||||
mut self,
|
||||
listener: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
|
||||
@@ -911,6 +923,9 @@ pub struct Interactivity {
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub location: Option<core::panic::Location<'static>>,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub debug_selector: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -980,6 +995,14 @@ impl Interactivity {
|
||||
let style = self.compute_style(Some(bounds), element_state, cx);
|
||||
let z_index = style.z_index.unwrap_or(0);
|
||||
|
||||
#[cfg(any(feature = "test-support", test))]
|
||||
if let Some(debug_selector) = &self.debug_selector {
|
||||
cx.window
|
||||
.next_frame
|
||||
.debug_bounds
|
||||
.insert(debug_selector.clone(), bounds);
|
||||
}
|
||||
|
||||
let paint_hover_group_handler = |cx: &mut WindowContext| {
|
||||
let hover_group_bounds = self
|
||||
.group_hover_style
|
||||
|
||||
@@ -30,6 +30,7 @@ struct StateInner {
|
||||
logical_scroll_top: Option<ListOffset>,
|
||||
alignment: ListAlignment,
|
||||
overdraw: Pixels,
|
||||
reset: bool,
|
||||
#[allow(clippy::type_complexity)]
|
||||
scroll_handler: Option<Box<dyn FnMut(&ListScrollEvent, &mut WindowContext)>>,
|
||||
}
|
||||
@@ -92,11 +93,17 @@ impl ListState {
|
||||
alignment: orientation,
|
||||
overdraw,
|
||||
scroll_handler: None,
|
||||
reset: false,
|
||||
})))
|
||||
}
|
||||
|
||||
/// Reset this instantiation of the list state.
|
||||
///
|
||||
/// Note that this will cause scroll events to be dropped until the next paint.
|
||||
pub fn reset(&self, element_count: usize) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
state.reset = true;
|
||||
|
||||
state.logical_scroll_top = None;
|
||||
state.items = SumTree::new();
|
||||
state
|
||||
@@ -152,11 +159,13 @@ impl ListState {
|
||||
scroll_top.item_ix = item_count;
|
||||
scroll_top.offset_in_item = px(0.);
|
||||
}
|
||||
|
||||
state.logical_scroll_top = Some(scroll_top);
|
||||
}
|
||||
|
||||
pub fn scroll_to_reveal_item(&self, ix: usize) {
|
||||
let state = &mut *self.0.borrow_mut();
|
||||
|
||||
let mut scroll_top = state.logical_scroll_top();
|
||||
let height = state
|
||||
.last_layout_bounds
|
||||
@@ -187,9 +196,9 @@ impl ListState {
|
||||
/// Get the bounds for the given item in window coordinates.
|
||||
pub fn bounds_for_item(&self, ix: usize) -> Option<Bounds<Pixels>> {
|
||||
let state = &*self.0.borrow();
|
||||
|
||||
let bounds = state.last_layout_bounds.unwrap_or_default();
|
||||
let scroll_top = state.logical_scroll_top();
|
||||
|
||||
if ix < scroll_top.item_ix {
|
||||
return None;
|
||||
}
|
||||
@@ -230,6 +239,12 @@ impl StateInner {
|
||||
delta: Point<Pixels>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
// Drop scroll events after a reset, since we can't calculate
|
||||
// the new logical scroll top without the item heights
|
||||
if self.reset {
|
||||
return;
|
||||
}
|
||||
|
||||
let scroll_max = (self.items.summary().height - height).max(px(0.));
|
||||
let new_scroll_top = (self.scroll_top(scroll_top) - delta.y)
|
||||
.max(px(0.))
|
||||
@@ -325,6 +340,8 @@ impl Element for List {
|
||||
) {
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
|
||||
state.reset = false;
|
||||
|
||||
// If the width of the list has changed, invalidate all cached item heights
|
||||
if state.last_layout_bounds.map_or(true, |last_bounds| {
|
||||
last_bounds.size.width != bounds.size.width
|
||||
@@ -346,8 +363,9 @@ impl Element for List {
|
||||
height: AvailableSpace::MinContent,
|
||||
};
|
||||
|
||||
// Render items after the scroll top, including those in the trailing overdraw
|
||||
let mut cursor = old_items.cursor::<Count>();
|
||||
|
||||
// Render items after the scroll top, including those in the trailing overdraw
|
||||
cursor.seek(&Count(scroll_top.item_ix), Bias::Right, &());
|
||||
for (ix, item) in cursor.by_ref().enumerate() {
|
||||
let visible_height = rendered_height - scroll_top.offset_in_item;
|
||||
@@ -461,6 +479,7 @@ impl Element for List {
|
||||
|
||||
let list_state = self.state.clone();
|
||||
let height = bounds.size.height;
|
||||
|
||||
cx.on_mouse_event(move |event: &ScrollWheelEvent, phase, cx| {
|
||||
if phase == DispatchPhase::Bubble
|
||||
&& bounds.contains(&event.position)
|
||||
@@ -562,3 +581,49 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
|
||||
self.0.partial_cmp(&other.height).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use gpui::{ScrollDelta, ScrollWheelEvent};
|
||||
|
||||
use crate::{self as gpui, TestAppContext};
|
||||
|
||||
#[gpui::test]
|
||||
fn test_reset_after_paint_before_scroll(cx: &mut TestAppContext) {
|
||||
use crate::{div, list, point, px, size, Element, ListState, Styled};
|
||||
|
||||
let cx = cx.add_empty_window();
|
||||
|
||||
let state = ListState::new(5, crate::ListAlignment::Top, px(10.), |_, _| {
|
||||
div().h(px(10.)).w_full().into_any()
|
||||
});
|
||||
|
||||
// Ensure that the list is scrolled to the top
|
||||
state.scroll_to(gpui::ListOffset {
|
||||
item_ix: 0,
|
||||
offset_in_item: px(0.0),
|
||||
});
|
||||
|
||||
// Paint
|
||||
cx.draw(
|
||||
point(px(0.), px(0.)),
|
||||
size(px(100.), px(20.)).into(),
|
||||
|_| list(state.clone()).w_full().h_full().z_index(10).into_any(),
|
||||
);
|
||||
|
||||
// Reset
|
||||
state.reset(5);
|
||||
|
||||
// And then receive a scroll event _before_ the next paint
|
||||
cx.simulate_event(ScrollWheelEvent {
|
||||
position: point(px(1.), px(1.)),
|
||||
delta: ScrollDelta::Pixels(point(px(0.), px(-500.))),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
// Scroll position should stay at the top of the list
|
||||
assert_eq!(state.logical_scroll_top().item_ix, 0);
|
||||
assert_eq!(state.logical_scroll_top().offset_in_item, px(0.));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,9 +109,10 @@ type AnyFuture<R> = Pin<Box<dyn 'static + Send + Future<Output = R>>>;
|
||||
|
||||
/// BackgroundExecutor lets you run things on background threads.
|
||||
/// In production this is a thread pool with no ordering guarantees.
|
||||
/// In tests this is simalated by running tasks one by one in a deterministic
|
||||
/// In tests this is simulated by running tasks one by one in a deterministic
|
||||
/// (but arbitrary) order controlled by the `SEED` environment variable.
|
||||
impl BackgroundExecutor {
|
||||
#[doc(hidden)]
|
||||
pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
|
||||
Self { dispatcher }
|
||||
}
|
||||
@@ -149,7 +150,7 @@ impl BackgroundExecutor {
|
||||
Task::Spawned(task)
|
||||
}
|
||||
|
||||
/// Used by the test harness to run an async test in a syncronous fashion.
|
||||
/// Used by the test harness to run an async test in a synchronous fashion.
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
#[track_caller]
|
||||
pub fn block_test<R>(&self, future: impl Future<Output = R>) -> R {
|
||||
@@ -276,7 +277,7 @@ impl BackgroundExecutor {
|
||||
|
||||
/// Returns a task that will complete after the given duration.
|
||||
/// Depending on other concurrent tasks the elapsed duration may be longer
|
||||
/// than reqested.
|
||||
/// than requested.
|
||||
pub fn timer(&self, duration: Duration) -> Task<()> {
|
||||
let (runnable, task) = async_task::spawn(async move {}, {
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
|
||||
+114
-61
@@ -1,8 +1,14 @@
|
||||
use crate::{
|
||||
div, point, Element, IntoElement, Keystroke, Modifiers, Pixels, Point, Render, ViewContext,
|
||||
point, seal::Sealed, IntoElement, Keystroke, Modifiers, Pixels, Point, Render, ViewContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{any::Any, fmt::Debug, marker::PhantomData, ops::Deref, path::PathBuf};
|
||||
use std::{any::Any, fmt::Debug, ops::Deref, path::PathBuf};
|
||||
|
||||
pub trait InputEvent: Sealed + 'static {
|
||||
fn to_platform_input(self) -> PlatformInput;
|
||||
}
|
||||
pub trait KeyEvent: InputEvent {}
|
||||
pub trait MouseEvent: InputEvent {}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct KeyDownEvent {
|
||||
@@ -10,16 +16,40 @@ pub struct KeyDownEvent {
|
||||
pub is_held: bool,
|
||||
}
|
||||
|
||||
impl Sealed for KeyDownEvent {}
|
||||
impl InputEvent for KeyDownEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::KeyDown(self)
|
||||
}
|
||||
}
|
||||
impl KeyEvent for KeyDownEvent {}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KeyUpEvent {
|
||||
pub keystroke: Keystroke,
|
||||
}
|
||||
|
||||
impl Sealed for KeyUpEvent {}
|
||||
impl InputEvent for KeyUpEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::KeyUp(self)
|
||||
}
|
||||
}
|
||||
impl KeyEvent for KeyUpEvent {}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ModifiersChangedEvent {
|
||||
pub modifiers: Modifiers,
|
||||
}
|
||||
|
||||
impl Sealed for ModifiersChangedEvent {}
|
||||
impl InputEvent for ModifiersChangedEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::ModifiersChanged(self)
|
||||
}
|
||||
}
|
||||
impl KeyEvent for ModifiersChangedEvent {}
|
||||
|
||||
impl Deref for ModifiersChangedEvent {
|
||||
type Target = Modifiers;
|
||||
|
||||
@@ -30,9 +60,10 @@ impl Deref for ModifiersChangedEvent {
|
||||
|
||||
/// The phase of a touch motion event.
|
||||
/// Based on the winit enum of the same name.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub enum TouchPhase {
|
||||
Started,
|
||||
#[default]
|
||||
Moved,
|
||||
Ended,
|
||||
}
|
||||
@@ -45,6 +76,14 @@ pub struct MouseDownEvent {
|
||||
pub click_count: usize,
|
||||
}
|
||||
|
||||
impl Sealed for MouseDownEvent {}
|
||||
impl InputEvent for MouseDownEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::MouseDown(self)
|
||||
}
|
||||
}
|
||||
impl MouseEvent for MouseDownEvent {}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct MouseUpEvent {
|
||||
pub button: MouseButton,
|
||||
@@ -53,38 +92,20 @@ pub struct MouseUpEvent {
|
||||
pub click_count: usize,
|
||||
}
|
||||
|
||||
impl Sealed for MouseUpEvent {}
|
||||
impl InputEvent for MouseUpEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::MouseUp(self)
|
||||
}
|
||||
}
|
||||
impl MouseEvent for MouseUpEvent {}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ClickEvent {
|
||||
pub down: MouseDownEvent,
|
||||
pub up: MouseUpEvent,
|
||||
}
|
||||
|
||||
pub struct Drag<S, R, V, E>
|
||||
where
|
||||
R: Fn(&mut V, &mut ViewContext<V>) -> E,
|
||||
V: 'static,
|
||||
E: IntoElement,
|
||||
{
|
||||
pub state: S,
|
||||
pub render_drag_handle: R,
|
||||
view_element_types: PhantomData<(V, E)>,
|
||||
}
|
||||
|
||||
impl<S, R, V, E> Drag<S, R, V, E>
|
||||
where
|
||||
R: Fn(&mut V, &mut ViewContext<V>) -> E,
|
||||
V: 'static,
|
||||
E: Element,
|
||||
{
|
||||
pub fn new(state: S, render_drag_handle: R) -> Self {
|
||||
Drag {
|
||||
state,
|
||||
render_drag_handle,
|
||||
view_element_types: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
|
||||
pub enum MouseButton {
|
||||
Left,
|
||||
@@ -130,13 +151,21 @@ pub struct MouseMoveEvent {
|
||||
pub modifiers: Modifiers,
|
||||
}
|
||||
|
||||
impl Sealed for MouseMoveEvent {}
|
||||
impl InputEvent for MouseMoveEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::MouseMove(self)
|
||||
}
|
||||
}
|
||||
impl MouseEvent for MouseMoveEvent {}
|
||||
|
||||
impl MouseMoveEvent {
|
||||
pub fn dragging(&self) -> bool {
|
||||
self.pressed_button == Some(MouseButton::Left)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ScrollWheelEvent {
|
||||
pub position: Point<Pixels>,
|
||||
pub delta: ScrollDelta,
|
||||
@@ -144,6 +173,14 @@ pub struct ScrollWheelEvent {
|
||||
pub touch_phase: TouchPhase,
|
||||
}
|
||||
|
||||
impl Sealed for ScrollWheelEvent {}
|
||||
impl InputEvent for ScrollWheelEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::ScrollWheel(self)
|
||||
}
|
||||
}
|
||||
impl MouseEvent for ScrollWheelEvent {}
|
||||
|
||||
impl Deref for ScrollWheelEvent {
|
||||
type Target = Modifiers;
|
||||
|
||||
@@ -201,6 +238,14 @@ pub struct MouseExitEvent {
|
||||
pub modifiers: Modifiers,
|
||||
}
|
||||
|
||||
impl Sealed for MouseExitEvent {}
|
||||
impl InputEvent for MouseExitEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::MouseExited(self)
|
||||
}
|
||||
}
|
||||
impl MouseEvent for MouseExitEvent {}
|
||||
|
||||
impl Deref for MouseExitEvent {
|
||||
type Target = Modifiers;
|
||||
|
||||
@@ -220,7 +265,7 @@ impl ExternalPaths {
|
||||
|
||||
impl Render for ExternalPaths {
|
||||
fn render(&mut self, _: &mut ViewContext<Self>) -> impl IntoElement {
|
||||
div() // Intentionally left empty because the platform will render icons for the dragged files
|
||||
() // Intentionally left empty because the platform will render icons for the dragged files
|
||||
}
|
||||
}
|
||||
|
||||
@@ -239,8 +284,16 @@ pub enum FileDropEvent {
|
||||
Exited,
|
||||
}
|
||||
|
||||
impl Sealed for FileDropEvent {}
|
||||
impl InputEvent for FileDropEvent {
|
||||
fn to_platform_input(self) -> PlatformInput {
|
||||
PlatformInput::FileDrop(self)
|
||||
}
|
||||
}
|
||||
impl MouseEvent for FileDropEvent {}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum InputEvent {
|
||||
pub enum PlatformInput {
|
||||
KeyDown(KeyDownEvent),
|
||||
KeyUp(KeyUpEvent),
|
||||
ModifiersChanged(ModifiersChangedEvent),
|
||||
@@ -252,19 +305,19 @@ pub enum InputEvent {
|
||||
FileDrop(FileDropEvent),
|
||||
}
|
||||
|
||||
impl InputEvent {
|
||||
impl PlatformInput {
|
||||
pub fn position(&self) -> Option<Point<Pixels>> {
|
||||
match self {
|
||||
InputEvent::KeyDown { .. } => None,
|
||||
InputEvent::KeyUp { .. } => None,
|
||||
InputEvent::ModifiersChanged { .. } => None,
|
||||
InputEvent::MouseDown(event) => Some(event.position),
|
||||
InputEvent::MouseUp(event) => Some(event.position),
|
||||
InputEvent::MouseMove(event) => Some(event.position),
|
||||
InputEvent::MouseExited(event) => Some(event.position),
|
||||
InputEvent::ScrollWheel(event) => Some(event.position),
|
||||
InputEvent::FileDrop(FileDropEvent::Exited) => None,
|
||||
InputEvent::FileDrop(
|
||||
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, .. },
|
||||
@@ -274,29 +327,29 @@ impl InputEvent {
|
||||
|
||||
pub fn mouse_event(&self) -> Option<&dyn Any> {
|
||||
match self {
|
||||
InputEvent::KeyDown { .. } => None,
|
||||
InputEvent::KeyUp { .. } => None,
|
||||
InputEvent::ModifiersChanged { .. } => None,
|
||||
InputEvent::MouseDown(event) => Some(event),
|
||||
InputEvent::MouseUp(event) => Some(event),
|
||||
InputEvent::MouseMove(event) => Some(event),
|
||||
InputEvent::MouseExited(event) => Some(event),
|
||||
InputEvent::ScrollWheel(event) => Some(event),
|
||||
InputEvent::FileDrop(event) => Some(event),
|
||||
PlatformInput::KeyDown { .. } => None,
|
||||
PlatformInput::KeyUp { .. } => None,
|
||||
PlatformInput::ModifiersChanged { .. } => None,
|
||||
PlatformInput::MouseDown(event) => Some(event),
|
||||
PlatformInput::MouseUp(event) => Some(event),
|
||||
PlatformInput::MouseMove(event) => Some(event),
|
||||
PlatformInput::MouseExited(event) => Some(event),
|
||||
PlatformInput::ScrollWheel(event) => Some(event),
|
||||
PlatformInput::FileDrop(event) => Some(event),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keyboard_event(&self) -> Option<&dyn Any> {
|
||||
match self {
|
||||
InputEvent::KeyDown(event) => Some(event),
|
||||
InputEvent::KeyUp(event) => Some(event),
|
||||
InputEvent::ModifiersChanged(event) => Some(event),
|
||||
InputEvent::MouseDown(_) => None,
|
||||
InputEvent::MouseUp(_) => None,
|
||||
InputEvent::MouseMove(_) => None,
|
||||
InputEvent::MouseExited(_) => None,
|
||||
InputEvent::ScrollWheel(_) => None,
|
||||
InputEvent::FileDrop(_) => None,
|
||||
PlatformInput::KeyDown(event) => Some(event),
|
||||
PlatformInput::KeyUp(event) => Some(event),
|
||||
PlatformInput::ModifiersChanged(event) => Some(event),
|
||||
PlatformInput::MouseDown(_) => None,
|
||||
PlatformInput::MouseUp(_) => None,
|
||||
PlatformInput::MouseMove(_) => None,
|
||||
PlatformInput::MouseExited(_) => None,
|
||||
PlatformInput::ScrollWheel(_) => None,
|
||||
PlatformInput::FileDrop(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,7 +209,6 @@ mod tests {
|
||||
);
|
||||
assert!(!matcher.has_pending_keystrokes());
|
||||
|
||||
eprintln!("PROBLEM AREA");
|
||||
// If a is prefixed, C will not be dispatched because there
|
||||
// was a pending binding for it
|
||||
assert_eq!(
|
||||
@@ -445,7 +444,7 @@ mod tests {
|
||||
KeyMatch::Some(vec![Box::new(Dollar)])
|
||||
);
|
||||
|
||||
// handle Brazillian quote (quote key then space key)
|
||||
// handle Brazilian quote (quote key then space key)
|
||||
assert_eq!(
|
||||
matcher.match_keystroke(
|
||||
&Keystroke::parse("space->\"").unwrap(),
|
||||
@@ -454,7 +453,7 @@ mod tests {
|
||||
KeyMatch::Some(vec![Box::new(Quote)])
|
||||
);
|
||||
|
||||
// handle ctrl+` on a brazillian keyboard
|
||||
// handle ctrl+` on a brazilian keyboard
|
||||
assert_eq!(
|
||||
matcher.match_keystroke(&Keystroke::parse("ctrl-->`").unwrap(), &[context_a.clone()]),
|
||||
KeyMatch::Some(vec![Box::new(Backtick)])
|
||||
|
||||
+24
-11
@@ -7,7 +7,7 @@ mod test;
|
||||
|
||||
use crate::{
|
||||
Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
|
||||
FontRun, ForegroundExecutor, GlobalPixels, GlyphId, InputEvent, Keymap, LineLayout, Pixels,
|
||||
FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout, Pixels, PlatformInput,
|
||||
Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
|
||||
Size, TaskLabel,
|
||||
};
|
||||
@@ -88,7 +88,7 @@ pub(crate) trait Platform: 'static {
|
||||
fn on_resign_active(&self, callback: Box<dyn FnMut()>);
|
||||
fn on_quit(&self, callback: Box<dyn FnMut()>);
|
||||
fn on_reopen(&self, callback: Box<dyn FnMut()>);
|
||||
fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
|
||||
fn on_event(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>);
|
||||
|
||||
fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap);
|
||||
fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>);
|
||||
@@ -114,15 +114,20 @@ pub(crate) trait Platform: 'static {
|
||||
fn delete_credentials(&self, url: &str) -> Result<()>;
|
||||
}
|
||||
|
||||
/// A handle to a platform's display, e.g. a monitor or laptop screen.
|
||||
pub trait PlatformDisplay: Send + Sync + Debug {
|
||||
/// Get the ID for this display
|
||||
fn id(&self) -> DisplayId;
|
||||
|
||||
/// Returns a stable identifier for this display that can be persisted and used
|
||||
/// across system restarts.
|
||||
fn uuid(&self) -> Result<Uuid>;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// Get the bounds for this display
|
||||
fn bounds(&self) -> Bounds<GlobalPixels>;
|
||||
}
|
||||
|
||||
/// An opaque identifier for a hardware display
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub struct DisplayId(pub(crate) u32);
|
||||
|
||||
@@ -134,7 +139,7 @@ impl Debug for DisplayId {
|
||||
|
||||
unsafe impl Send for DisplayId {}
|
||||
|
||||
pub trait PlatformWindow {
|
||||
pub(crate) trait PlatformWindow {
|
||||
fn bounds(&self) -> WindowBounds;
|
||||
fn content_size(&self) -> Size<Pixels>;
|
||||
fn scale_factor(&self) -> f32;
|
||||
@@ -155,7 +160,7 @@ pub trait PlatformWindow {
|
||||
fn zoom(&self);
|
||||
fn toggle_full_screen(&self);
|
||||
fn on_request_frame(&self, callback: Box<dyn FnMut()>);
|
||||
fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>);
|
||||
fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>);
|
||||
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
|
||||
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
|
||||
fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>);
|
||||
@@ -175,6 +180,9 @@ pub trait PlatformWindow {
|
||||
}
|
||||
}
|
||||
|
||||
/// This type is public so that our test macro can generate and use it, but it should not
|
||||
/// be considered part of our public API.
|
||||
#[doc(hidden)]
|
||||
pub trait PlatformDispatcher: Send + Sync {
|
||||
fn is_main_thread(&self) -> bool;
|
||||
fn dispatch(&self, runnable: Runnable, label: Option<TaskLabel>);
|
||||
@@ -190,9 +198,10 @@ pub trait PlatformDispatcher: Send + Sync {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PlatformTextSystem: Send + Sync {
|
||||
pub(crate) trait PlatformTextSystem: Send + Sync {
|
||||
fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()>;
|
||||
fn all_font_names(&self) -> Vec<String>;
|
||||
fn all_font_families(&self) -> Vec<String>;
|
||||
fn font_id(&self, descriptor: &Font) -> Result<FontId>;
|
||||
fn font_metrics(&self, font_id: FontId) -> FontMetrics;
|
||||
fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>>;
|
||||
@@ -214,15 +223,21 @@ pub trait PlatformTextSystem: Send + Sync {
|
||||
) -> Vec<usize>;
|
||||
}
|
||||
|
||||
/// Basic metadata about the current application and operating system.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AppMetadata {
|
||||
/// The name of the current operating system
|
||||
pub os_name: &'static str,
|
||||
|
||||
/// The operating system's version
|
||||
pub os_version: Option<SemanticVersion>,
|
||||
|
||||
/// The current version of the application
|
||||
pub app_version: Option<SemanticVersion>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
pub enum AtlasKey {
|
||||
pub(crate) enum AtlasKey {
|
||||
Glyph(RenderGlyphParams),
|
||||
Svg(RenderSvgParams),
|
||||
Image(RenderImageParams),
|
||||
@@ -262,19 +277,17 @@ impl From<RenderImageParams> for AtlasKey {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PlatformAtlas: Send + Sync {
|
||||
pub(crate) trait PlatformAtlas: Send + Sync {
|
||||
fn get_or_insert_with<'a>(
|
||||
&self,
|
||||
key: &AtlasKey,
|
||||
build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
|
||||
) -> Result<AtlasTile>;
|
||||
|
||||
fn clear(&self);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(C)]
|
||||
pub struct AtlasTile {
|
||||
pub(crate) struct AtlasTile {
|
||||
pub(crate) texture_id: AtlasTextureId,
|
||||
pub(crate) tile_id: TileId,
|
||||
pub(crate) bounds: Bounds<DevicePixels>,
|
||||
|
||||
@@ -19,7 +19,7 @@ impl Keystroke {
|
||||
// the ime_key or the key. On some non-US keyboards keys we use in our
|
||||
// bindings are behind option (for example `$` is typed `alt-ç` on a Czech keyboard),
|
||||
// and on some keyboards the IME handler converts a sequence of keys into a
|
||||
// specific character (for example `"` is typed as `" space` on a brazillian keyboard).
|
||||
// specific character (for example `"` is typed as `" space` on a brazilian keyboard).
|
||||
pub fn match_candidates(&self) -> SmallVec<[Keystroke; 2]> {
|
||||
let mut possibilities = SmallVec::new();
|
||||
match self.ime_key.as_ref() {
|
||||
|
||||
@@ -10,7 +10,7 @@ mod open_type;
|
||||
mod platform;
|
||||
mod text_system;
|
||||
mod window;
|
||||
mod window_appearence;
|
||||
mod window_appearance;
|
||||
|
||||
use crate::{px, size, GlobalPixels, Pixels, Size};
|
||||
use cocoa::{
|
||||
|
||||
@@ -11,7 +11,6 @@ use core_graphics::{
|
||||
geometry::{CGPoint, CGRect, CGSize},
|
||||
};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use std::any::Any;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -154,10 +153,6 @@ impl PlatformDisplay for MacDisplay {
|
||||
]))
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn bounds(&self) -> Bounds<GlobalPixels> {
|
||||
unsafe {
|
||||
let native_bounds = CGDisplayBounds(self.0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
point, px, InputEvent, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent,
|
||||
MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection,
|
||||
Pixels, ScrollDelta, ScrollWheelEvent, TouchPhase,
|
||||
point, px, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton,
|
||||
MouseDownEvent, MouseExitEvent, MouseMoveEvent, MouseUpEvent, NavigationDirection, Pixels,
|
||||
PlatformInput, ScrollDelta, ScrollWheelEvent, TouchPhase,
|
||||
};
|
||||
use cocoa::{
|
||||
appkit::{NSEvent, NSEventModifierFlags, NSEventPhase, NSEventType},
|
||||
@@ -82,7 +82,7 @@ unsafe fn read_modifiers(native_event: id) -> Modifiers {
|
||||
}
|
||||
}
|
||||
|
||||
impl InputEvent {
|
||||
impl PlatformInput {
|
||||
pub unsafe fn from_native(native_event: id, window_height: Option<Pixels>) -> Option<Self> {
|
||||
let event_type = native_event.eventType();
|
||||
|
||||
|
||||
@@ -74,20 +74,6 @@ impl PlatformAtlas for MetalAtlas {
|
||||
Ok(tile)
|
||||
}
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
let mut lock = self.0.lock();
|
||||
lock.tiles_by_key.clear();
|
||||
for texture in &mut lock.monochrome_textures {
|
||||
texture.clear();
|
||||
}
|
||||
for texture in &mut lock.polychrome_textures {
|
||||
texture.clear();
|
||||
}
|
||||
for texture in &mut lock.path_textures {
|
||||
texture.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MetalAtlasState {
|
||||
|
||||
@@ -14,6 +14,7 @@ use foreign_types::ForeignType;
|
||||
use media::core_video::CVMetalTextureCache;
|
||||
use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
|
||||
use objc::{self, msg_send, sel, sel_impl};
|
||||
use smallvec::SmallVec;
|
||||
use std::{ffi::c_void, mem, ptr, sync::Arc};
|
||||
|
||||
const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
|
||||
@@ -81,7 +82,7 @@ impl MetalRenderer {
|
||||
];
|
||||
let unit_vertices = device.new_buffer_with_data(
|
||||
unit_vertices.as_ptr() as *const c_void,
|
||||
(unit_vertices.len() * mem::size_of::<u64>()) as u64,
|
||||
mem::size_of_val(&unit_vertices) as u64,
|
||||
MTLResourceOptions::StorageModeManaged,
|
||||
);
|
||||
let instances = device.new_buffer(
|
||||
@@ -339,7 +340,8 @@ impl MetalRenderer {
|
||||
|
||||
for (texture_id, vertices) in vertices_by_texture_id {
|
||||
align_offset(offset);
|
||||
let next_offset = *offset + vertices.len() * mem::size_of::<PathVertex<ScaledPixels>>();
|
||||
let vertices_bytes_len = mem::size_of_val(vertices.as_slice());
|
||||
let next_offset = *offset + vertices_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return None;
|
||||
}
|
||||
@@ -372,7 +374,6 @@ impl MetalRenderer {
|
||||
&texture_size as *const Size<DevicePixels> as *const _,
|
||||
);
|
||||
|
||||
let vertices_bytes_len = mem::size_of::<PathVertex<ScaledPixels>>() * vertices.len();
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
@@ -429,7 +430,7 @@ impl MetalRenderer {
|
||||
&viewport_size as *const Size<DevicePixels> as *const _,
|
||||
);
|
||||
|
||||
let shadow_bytes_len = std::mem::size_of_val(shadows);
|
||||
let shadow_bytes_len = mem::size_of_val(shadows);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
|
||||
let next_offset = *offset + shadow_bytes_len;
|
||||
@@ -490,7 +491,7 @@ impl MetalRenderer {
|
||||
&viewport_size as *const Size<DevicePixels> as *const _,
|
||||
);
|
||||
|
||||
let quad_bytes_len = std::mem::size_of_val(quads);
|
||||
let quad_bytes_len = mem::size_of_val(quads);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
|
||||
let next_offset = *offset + quad_bytes_len;
|
||||
@@ -537,7 +538,7 @@ impl MetalRenderer {
|
||||
);
|
||||
|
||||
let mut prev_texture_id = None;
|
||||
let mut sprites = Vec::new();
|
||||
let mut sprites = SmallVec::<[_; 1]>::new();
|
||||
let mut paths_and_tiles = paths
|
||||
.iter()
|
||||
.map(|path| (path, tiles_by_path_id.get(&path.id).unwrap()))
|
||||
@@ -590,7 +591,7 @@ impl MetalRenderer {
|
||||
command_encoder
|
||||
.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
|
||||
|
||||
let sprite_bytes_len = mem::size_of::<MonochromeSprite>() * sprites.len();
|
||||
let sprite_bytes_len = mem::size_of_val(sprites.as_slice());
|
||||
let next_offset = *offset + sprite_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
@@ -655,21 +656,22 @@ impl MetalRenderer {
|
||||
&viewport_size as *const Size<DevicePixels> as *const _,
|
||||
);
|
||||
|
||||
let quad_bytes_len = std::mem::size_of_val(underlines);
|
||||
let underline_bytes_len = mem::size_of_val(underlines);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
|
||||
let next_offset = *offset + underline_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
underlines.as_ptr() as *const u8,
|
||||
buffer_contents,
|
||||
quad_bytes_len,
|
||||
underline_bytes_len,
|
||||
);
|
||||
}
|
||||
|
||||
let next_offset = *offset + quad_bytes_len;
|
||||
if next_offset > INSTANCE_BUFFER_SIZE {
|
||||
return false;
|
||||
}
|
||||
|
||||
command_encoder.draw_primitives_instanced(
|
||||
metal::MTLPrimitiveType::Triangle,
|
||||
0,
|
||||
@@ -726,7 +728,7 @@ impl MetalRenderer {
|
||||
);
|
||||
command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
|
||||
|
||||
let sprite_bytes_len = std::mem::size_of_val(sprites);
|
||||
let sprite_bytes_len = mem::size_of_val(sprites);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
|
||||
let next_offset = *offset + sprite_bytes_len;
|
||||
@@ -798,7 +800,7 @@ impl MetalRenderer {
|
||||
);
|
||||
command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
|
||||
|
||||
let sprite_bytes_len = std::mem::size_of_val(sprites);
|
||||
let sprite_bytes_len = mem::size_of_val(sprites);
|
||||
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
|
||||
|
||||
let next_offset = *offset + sprite_bytes_len;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use super::{events::key_to_native, BoolExt};
|
||||
use crate::{
|
||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
||||
ForegroundExecutor, InputEvent, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker,
|
||||
MacTextSystem, MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay,
|
||||
ForegroundExecutor, Keymap, MacDispatcher, MacDisplay, MacDisplayLinker, MacTextSystem,
|
||||
MacWindow, Menu, MenuItem, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
|
||||
PlatformTextSystem, PlatformWindow, Result, SemanticVersion, VideoTimestamp, WindowOptions,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
@@ -153,7 +153,7 @@ pub struct MacPlatformState {
|
||||
resign_active: Option<Box<dyn FnMut()>>,
|
||||
reopen: Option<Box<dyn FnMut()>>,
|
||||
quit: Option<Box<dyn FnMut()>>,
|
||||
event: Option<Box<dyn FnMut(InputEvent) -> bool>>,
|
||||
event: Option<Box<dyn FnMut(PlatformInput) -> bool>>,
|
||||
menu_command: Option<Box<dyn FnMut(&dyn Action)>>,
|
||||
validate_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
|
||||
will_open_menu: Option<Box<dyn FnMut()>>,
|
||||
@@ -637,7 +637,7 @@ impl Platform for MacPlatform {
|
||||
self.0.lock().reopen = Some(callback);
|
||||
}
|
||||
|
||||
fn on_event(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
|
||||
fn on_event(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
|
||||
self.0.lock().event = Some(callback);
|
||||
}
|
||||
|
||||
@@ -976,7 +976,7 @@ unsafe fn get_mac_platform(object: &mut Object) -> &MacPlatform {
|
||||
|
||||
extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
|
||||
unsafe {
|
||||
if let Some(event) = InputEvent::from_native(native_event, None) {
|
||||
if let Some(event) = PlatformInput::from_native(native_event, None) {
|
||||
let platform = get_mac_platform(this);
|
||||
let mut lock = platform.0.lock();
|
||||
if let Some(mut callback) = lock.event.take() {
|
||||
|
||||
@@ -85,11 +85,24 @@ impl PlatformTextSystem for MacTextSystem {
|
||||
};
|
||||
let mut names = BTreeSet::new();
|
||||
for descriptor in descriptors.into_iter() {
|
||||
names.insert(descriptor.display_name());
|
||||
names.insert(descriptor.font_name());
|
||||
names.insert(descriptor.family_name());
|
||||
names.insert(descriptor.style_name());
|
||||
}
|
||||
if let Ok(fonts_in_memory) = self.0.read().memory_source.all_families() {
|
||||
names.extend(fonts_in_memory);
|
||||
}
|
||||
names.into_iter().collect()
|
||||
}
|
||||
|
||||
fn all_font_families(&self) -> Vec<String> {
|
||||
self.0
|
||||
.read()
|
||||
.system_source
|
||||
.all_families()
|
||||
.expect("core text should never return an error")
|
||||
}
|
||||
|
||||
fn font_id(&self, font: &Font) -> Result<FontId> {
|
||||
let lock = self.0.upgradable_read();
|
||||
if let Some(font_id) = lock.font_selections.get(font) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::{display_bounds_from_native, ns_string, MacDisplay, MetalRenderer, NSRange};
|
||||
use crate::{
|
||||
display_bounds_to_native, point, px, size, AnyWindowHandle, Bounds, ExternalPaths,
|
||||
FileDropEvent, ForegroundExecutor, GlobalPixels, InputEvent, KeyDownEvent, Keystroke,
|
||||
Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
|
||||
Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
|
||||
FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent, Keystroke, Modifiers,
|
||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
||||
PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
@@ -319,7 +319,7 @@ struct MacWindowState {
|
||||
renderer: MetalRenderer,
|
||||
kind: WindowKind,
|
||||
request_frame_callback: Option<Box<dyn FnMut()>>,
|
||||
event_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
|
||||
event_callback: Option<Box<dyn FnMut(PlatformInput) -> bool>>,
|
||||
activate_callback: Option<Box<dyn FnMut(bool)>>,
|
||||
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
|
||||
@@ -333,7 +333,7 @@ struct MacWindowState {
|
||||
synthetic_drag_counter: usize,
|
||||
last_fresh_keydown: Option<Keystroke>,
|
||||
traffic_light_position: Option<Point<Pixels>>,
|
||||
previous_modifiers_changed_event: Option<InputEvent>,
|
||||
previous_modifiers_changed_event: Option<PlatformInput>,
|
||||
// State tracking what the IME did after the last request
|
||||
ime_state: ImeState,
|
||||
// Retains the last IME Text
|
||||
@@ -928,7 +928,7 @@ impl PlatformWindow for MacWindow {
|
||||
self.0.as_ref().lock().request_frame_callback = Some(callback);
|
||||
}
|
||||
|
||||
fn on_input(&self, callback: Box<dyn FnMut(InputEvent) -> bool>) {
|
||||
fn on_input(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
|
||||
self.0.as_ref().lock().event_callback = Some(callback);
|
||||
}
|
||||
|
||||
@@ -1053,9 +1053,9 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
|
||||
let mut lock = window_state.as_ref().lock();
|
||||
|
||||
let window_height = lock.content_size().height;
|
||||
let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
|
||||
let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
|
||||
|
||||
if let Some(InputEvent::KeyDown(event)) = event {
|
||||
if let Some(PlatformInput::KeyDown(event)) = event {
|
||||
// For certain keystrokes, macOS will first dispatch a "key equivalent" event.
|
||||
// If that event isn't handled, it will then dispatch a "key down" event. GPUI
|
||||
// makes no distinction between these two types of events, so we need to ignore
|
||||
@@ -1102,7 +1102,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
|
||||
.flatten()
|
||||
.is_some();
|
||||
if !is_composing {
|
||||
handled = callback(InputEvent::KeyDown(event));
|
||||
handled = callback(PlatformInput::KeyDown(event));
|
||||
}
|
||||
|
||||
if !handled {
|
||||
@@ -1146,11 +1146,11 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||
let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
|
||||
|
||||
let window_height = lock.content_size().height;
|
||||
let event = unsafe { InputEvent::from_native(native_event, Some(window_height)) };
|
||||
let event = unsafe { PlatformInput::from_native(native_event, Some(window_height)) };
|
||||
|
||||
if let Some(mut event) = event {
|
||||
match &mut event {
|
||||
InputEvent::MouseDown(
|
||||
PlatformInput::MouseDown(
|
||||
event @ MouseDownEvent {
|
||||
button: MouseButton::Left,
|
||||
modifiers: Modifiers { control: true, .. },
|
||||
@@ -1172,7 +1172,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||
// Because we map a ctrl-left_down to a right_down -> right_up let's ignore
|
||||
// the ctrl-left_up to avoid having a mismatch in button down/up events if the
|
||||
// user is still holding ctrl when releasing the left mouse button
|
||||
InputEvent::MouseUp(
|
||||
PlatformInput::MouseUp(
|
||||
event @ MouseUpEvent {
|
||||
button: MouseButton::Left,
|
||||
modifiers: Modifiers { control: true, .. },
|
||||
@@ -1194,7 +1194,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||
};
|
||||
|
||||
match &event {
|
||||
InputEvent::MouseMove(
|
||||
PlatformInput::MouseMove(
|
||||
event @ MouseMoveEvent {
|
||||
pressed_button: Some(_),
|
||||
..
|
||||
@@ -1216,15 +1216,15 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
|
||||
}
|
||||
}
|
||||
|
||||
InputEvent::MouseMove(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
|
||||
PlatformInput::MouseMove(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
|
||||
|
||||
InputEvent::MouseUp(MouseUpEvent { .. }) => {
|
||||
PlatformInput::MouseUp(MouseUpEvent { .. }) => {
|
||||
lock.synthetic_drag_counter += 1;
|
||||
}
|
||||
|
||||
InputEvent::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
|
||||
PlatformInput::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
|
||||
// Only raise modifiers changed event when they have actually changed
|
||||
if let Some(InputEvent::ModifiersChanged(ModifiersChangedEvent {
|
||||
if let Some(PlatformInput::ModifiersChanged(ModifiersChangedEvent {
|
||||
modifiers: prev_modifiers,
|
||||
})) = &lock.previous_modifiers_changed_event
|
||||
{
|
||||
@@ -1258,7 +1258,7 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
|
||||
key: ".".into(),
|
||||
ime_key: None,
|
||||
};
|
||||
let event = InputEvent::KeyDown(KeyDownEvent {
|
||||
let event = PlatformInput::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held: false,
|
||||
});
|
||||
@@ -1655,7 +1655,7 @@ extern "C" fn dragging_entered(this: &Object, _: Sel, dragging_info: id) -> NSDr
|
||||
if send_new_event(&window_state, {
|
||||
let position = drag_event_position(&window_state, dragging_info);
|
||||
let paths = external_paths_from_event(dragging_info);
|
||||
InputEvent::FileDrop(FileDropEvent::Entered { position, paths })
|
||||
PlatformInput::FileDrop(FileDropEvent::Entered { position, paths })
|
||||
}) {
|
||||
window_state.lock().external_files_dragged = true;
|
||||
NSDragOperationCopy
|
||||
@@ -1669,7 +1669,7 @@ extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDr
|
||||
let position = drag_event_position(&window_state, dragging_info);
|
||||
if send_new_event(
|
||||
&window_state,
|
||||
InputEvent::FileDrop(FileDropEvent::Pending { position }),
|
||||
PlatformInput::FileDrop(FileDropEvent::Pending { position }),
|
||||
) {
|
||||
NSDragOperationCopy
|
||||
} else {
|
||||
@@ -1679,7 +1679,10 @@ extern "C" fn dragging_updated(this: &Object, _: Sel, dragging_info: id) -> NSDr
|
||||
|
||||
extern "C" fn dragging_exited(this: &Object, _: Sel, _: id) {
|
||||
let window_state = unsafe { get_window_state(this) };
|
||||
send_new_event(&window_state, InputEvent::FileDrop(FileDropEvent::Exited));
|
||||
send_new_event(
|
||||
&window_state,
|
||||
PlatformInput::FileDrop(FileDropEvent::Exited),
|
||||
);
|
||||
window_state.lock().external_files_dragged = false;
|
||||
}
|
||||
|
||||
@@ -1688,7 +1691,7 @@ extern "C" fn perform_drag_operation(this: &Object, _: Sel, dragging_info: id) -
|
||||
let position = drag_event_position(&window_state, dragging_info);
|
||||
if send_new_event(
|
||||
&window_state,
|
||||
InputEvent::FileDrop(FileDropEvent::Submit { position }),
|
||||
PlatformInput::FileDrop(FileDropEvent::Submit { position }),
|
||||
) {
|
||||
YES
|
||||
} else {
|
||||
@@ -1712,7 +1715,10 @@ fn external_paths_from_event(dragging_info: *mut Object) -> ExternalPaths {
|
||||
|
||||
extern "C" fn conclude_drag_operation(this: &Object, _: Sel, _: id) {
|
||||
let window_state = unsafe { get_window_state(this) };
|
||||
send_new_event(&window_state, InputEvent::FileDrop(FileDropEvent::Exited));
|
||||
send_new_event(
|
||||
&window_state,
|
||||
PlatformInput::FileDrop(FileDropEvent::Exited),
|
||||
);
|
||||
}
|
||||
|
||||
async fn synthetic_drag(
|
||||
@@ -1727,7 +1733,7 @@ async fn synthetic_drag(
|
||||
if lock.synthetic_drag_counter == drag_id {
|
||||
if let Some(mut callback) = lock.event_callback.take() {
|
||||
drop(lock);
|
||||
callback(InputEvent::MouseMove(event.clone()));
|
||||
callback(PlatformInput::MouseMove(event.clone()));
|
||||
window_state.lock().event_callback = Some(callback);
|
||||
}
|
||||
} else {
|
||||
@@ -1737,7 +1743,7 @@ async fn synthetic_drag(
|
||||
}
|
||||
}
|
||||
|
||||
fn send_new_event(window_state_lock: &Mutex<MacWindowState>, e: InputEvent) -> bool {
|
||||
fn send_new_event(window_state_lock: &Mutex<MacWindowState>, e: PlatformInput) -> bool {
|
||||
let window_state = window_state_lock.lock().event_callback.take();
|
||||
if let Some(mut callback) = window_state {
|
||||
callback(e);
|
||||
|
||||
@@ -31,10 +31,6 @@ impl PlatformDisplay for TestDisplay {
|
||||
Ok(self.uuid)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn bounds(&self) -> crate::Bounds<crate::GlobalPixels> {
|
||||
self.bounds
|
||||
}
|
||||
|
||||
@@ -239,7 +239,7 @@ impl Platform for TestPlatform {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn on_event(&self, _callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
|
||||
fn on_event(&self, _callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, InputEvent, KeyDownEvent,
|
||||
Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
|
||||
Size, TestPlatform, TileId, WindowAppearance, WindowBounds, WindowOptions,
|
||||
px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, KeyDownEvent, Keystroke,
|
||||
Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow,
|
||||
Point, Size, TestPlatform, TileId, WindowAppearance, WindowBounds, WindowOptions,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use parking_lot::Mutex;
|
||||
@@ -19,7 +19,7 @@ pub struct TestWindowState {
|
||||
platform: Weak<TestPlatform>,
|
||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||
pub(crate) should_close_handler: Option<Box<dyn FnMut() -> bool>>,
|
||||
input_callback: Option<Box<dyn FnMut(InputEvent) -> bool>>,
|
||||
input_callback: Option<Box<dyn FnMut(PlatformInput) -> bool>>,
|
||||
active_status_change_callback: Option<Box<dyn FnMut(bool)>>,
|
||||
resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
moved_callback: Option<Box<dyn FnMut()>>,
|
||||
@@ -85,7 +85,7 @@ impl TestWindow {
|
||||
self.0.lock().active_status_change_callback = Some(callback);
|
||||
}
|
||||
|
||||
pub fn simulate_input(&mut self, event: InputEvent) -> bool {
|
||||
pub fn simulate_input(&mut self, event: PlatformInput) -> bool {
|
||||
let mut lock = self.0.lock();
|
||||
let Some(mut callback) = lock.input_callback.take() else {
|
||||
return false;
|
||||
@@ -97,7 +97,7 @@ impl TestWindow {
|
||||
}
|
||||
|
||||
pub fn simulate_keystroke(&mut self, keystroke: Keystroke, is_held: bool) {
|
||||
if self.simulate_input(InputEvent::KeyDown(KeyDownEvent {
|
||||
if self.simulate_input(PlatformInput::KeyDown(KeyDownEvent {
|
||||
keystroke: keystroke.clone(),
|
||||
is_held,
|
||||
})) {
|
||||
@@ -220,7 +220,7 @@ impl PlatformWindow for TestWindow {
|
||||
|
||||
fn on_request_frame(&self, _callback: Box<dyn FnMut()>) {}
|
||||
|
||||
fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
|
||||
fn on_input(&self, callback: Box<dyn FnMut(crate::PlatformInput) -> bool>) {
|
||||
self.0.lock().input_callback = Some(callback)
|
||||
}
|
||||
|
||||
@@ -325,10 +325,4 @@ impl PlatformAtlas for TestAtlas {
|
||||
|
||||
Ok(state.tiles[key].clone())
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
let mut state = self.0.lock();
|
||||
state.tiles = HashMap::default();
|
||||
state.next_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-13
@@ -93,7 +93,7 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
|
||||
pub(crate) fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
|
||||
let primitive = primitive.into();
|
||||
let clipped_bounds = primitive
|
||||
.bounds()
|
||||
@@ -299,8 +299,8 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
|
||||
let first = orders_and_kinds[0];
|
||||
let second = orders_and_kinds[1];
|
||||
let (batch_kind, max_order) = if first.0.is_some() {
|
||||
(first.1, second.0.unwrap_or(u32::MAX))
|
||||
let (batch_kind, max_order_and_kind) = if first.0.is_some() {
|
||||
(first.1, (second.0.unwrap_or(u32::MAX), second.1))
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
@@ -312,7 +312,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.shadows_iter.next();
|
||||
while self
|
||||
.shadows_iter
|
||||
.next_if(|shadow| shadow.order < max_order)
|
||||
.next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
shadows_end += 1;
|
||||
@@ -328,7 +328,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.quads_iter.next();
|
||||
while self
|
||||
.quads_iter
|
||||
.next_if(|quad| quad.order < max_order)
|
||||
.next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
quads_end += 1;
|
||||
@@ -342,7 +342,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.paths_iter.next();
|
||||
while self
|
||||
.paths_iter
|
||||
.next_if(|path| path.order < max_order)
|
||||
.next_if(|path| (path.order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
paths_end += 1;
|
||||
@@ -356,7 +356,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.underlines_iter.next();
|
||||
while self
|
||||
.underlines_iter
|
||||
.next_if(|underline| underline.order < max_order)
|
||||
.next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
underlines_end += 1;
|
||||
@@ -374,7 +374,8 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
while self
|
||||
.monochrome_sprites_iter
|
||||
.next_if(|sprite| {
|
||||
sprite.order < max_order && sprite.tile.texture_id == texture_id
|
||||
(sprite.order, batch_kind) < max_order_and_kind
|
||||
&& sprite.tile.texture_id == texture_id
|
||||
})
|
||||
.is_some()
|
||||
{
|
||||
@@ -394,7 +395,8 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
while self
|
||||
.polychrome_sprites_iter
|
||||
.next_if(|sprite| {
|
||||
sprite.order < max_order && sprite.tile.texture_id == texture_id
|
||||
(sprite.order, batch_kind) < max_order_and_kind
|
||||
&& sprite.tile.texture_id == texture_id
|
||||
})
|
||||
.is_some()
|
||||
{
|
||||
@@ -412,7 +414,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.surfaces_iter.next();
|
||||
while self
|
||||
.surfaces_iter
|
||||
.next_if(|surface| surface.order < max_order)
|
||||
.next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
surfaces_end += 1;
|
||||
@@ -438,7 +440,7 @@ pub enum PrimitiveKind {
|
||||
Surface,
|
||||
}
|
||||
|
||||
pub enum Primitive {
|
||||
pub(crate) enum Primitive {
|
||||
Shadow(Shadow),
|
||||
Quad(Quad),
|
||||
Path(Path<ScaledPixels>),
|
||||
@@ -587,7 +589,7 @@ impl From<Shadow> for Primitive {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct MonochromeSprite {
|
||||
pub(crate) struct MonochromeSprite {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
@@ -620,7 +622,7 @@ impl From<MonochromeSprite> for Primitive {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub struct PolychromeSprite {
|
||||
pub(crate) struct PolychromeSprite {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
|
||||
@@ -42,7 +42,7 @@ pub struct Style {
|
||||
#[refineable]
|
||||
pub inset: Edges<Length>,
|
||||
|
||||
// Size properies
|
||||
// Size properties
|
||||
/// Sets the initial size of the item
|
||||
#[refineable]
|
||||
pub size: Size<Length>,
|
||||
@@ -79,7 +79,7 @@ pub struct Style {
|
||||
#[refineable]
|
||||
pub gap: Size<DefiniteLength>,
|
||||
|
||||
// Flexbox properies
|
||||
// Flexbox properties
|
||||
/// Which direction does the main axis flow in?
|
||||
pub flex_direction: FlexDirection,
|
||||
/// Should elements wrap, or stay in a single line?
|
||||
@@ -386,7 +386,7 @@ impl Style {
|
||||
|
||||
let background_color = self.background.as_ref().and_then(Fill::color);
|
||||
if background_color.map_or(false, |color| !color.is_transparent()) {
|
||||
cx.with_z_index(1, |cx| {
|
||||
cx.with_z_index(0, |cx| {
|
||||
let mut border_color = background_color.unwrap_or_default();
|
||||
border_color.a = 0.;
|
||||
cx.paint_quad(quad(
|
||||
@@ -399,12 +399,12 @@ impl Style {
|
||||
});
|
||||
}
|
||||
|
||||
cx.with_z_index(2, |cx| {
|
||||
cx.with_z_index(0, |cx| {
|
||||
continuation(cx);
|
||||
});
|
||||
|
||||
if self.is_border_visible() {
|
||||
cx.with_z_index(3, |cx| {
|
||||
cx.with_z_index(0, |cx| {
|
||||
let corner_radii = self.corner_radii.to_pixels(bounds.size, rem_size);
|
||||
let border_widths = self.border_widths.to_pixels(rem_size);
|
||||
let max_border_width = border_widths.max();
|
||||
@@ -502,7 +502,7 @@ impl Default for Style {
|
||||
max_size: Size::auto(),
|
||||
aspect_ratio: None,
|
||||
gap: Size::default(),
|
||||
// Aligment
|
||||
// Alignment
|
||||
align_items: None,
|
||||
align_self: None,
|
||||
align_content: None,
|
||||
|
||||
@@ -13,7 +13,7 @@ use crate::{
|
||||
SharedString, Size, UnderlineStyle,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
use collections::{BTreeSet, FxHashMap, FxHashSet};
|
||||
use core::fmt;
|
||||
use itertools::Itertools;
|
||||
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
|
||||
@@ -47,7 +47,7 @@ pub struct TextSystem {
|
||||
}
|
||||
|
||||
impl TextSystem {
|
||||
pub fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
|
||||
pub(crate) fn new(platform_text_system: Arc<dyn PlatformTextSystem>) -> Self {
|
||||
TextSystem {
|
||||
line_layout_cache: Arc::new(LineLayoutCache::new(platform_text_system.clone())),
|
||||
platform_text_system,
|
||||
@@ -66,15 +66,18 @@ impl TextSystem {
|
||||
}
|
||||
|
||||
pub fn all_font_names(&self) -> Vec<String> {
|
||||
let mut families = self.platform_text_system.all_font_names();
|
||||
families.append(
|
||||
&mut self
|
||||
.fallback_font_stack
|
||||
let mut names: BTreeSet<_> = self
|
||||
.platform_text_system
|
||||
.all_font_names()
|
||||
.into_iter()
|
||||
.collect();
|
||||
names.extend(self.platform_text_system.all_font_families().into_iter());
|
||||
names.extend(
|
||||
self.fallback_font_stack
|
||||
.iter()
|
||||
.map(|font| font.family.to_string())
|
||||
.collect(),
|
||||
.map(|font| font.family.to_string()),
|
||||
);
|
||||
families
|
||||
names.into_iter().collect()
|
||||
}
|
||||
pub fn add_fonts(&self, fonts: &[Arc<Vec<u8>>]) -> Result<()> {
|
||||
self.platform_text_system.add_fonts(fonts)
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct LineWrapper {
|
||||
impl LineWrapper {
|
||||
pub const MAX_INDENT: u32 = 256;
|
||||
|
||||
pub fn new(
|
||||
pub(crate) fn new(
|
||||
font_id: FontId,
|
||||
font_size: Pixels,
|
||||
text_system: Arc<dyn PlatformTextSystem>,
|
||||
|
||||
+28
-2
@@ -1,3 +1,5 @@
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use crate::{
|
||||
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
|
||||
Bounds, ContentMask, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView,
|
||||
@@ -11,12 +13,16 @@ use std::{
|
||||
hash::{Hash, Hasher},
|
||||
};
|
||||
|
||||
/// A view is a piece of state that can be presented on screen by implementing the [Render] trait.
|
||||
/// Views implement [Element] and can composed with other views, and every window is created with a root view.
|
||||
pub struct View<V> {
|
||||
/// A view is just a [Model] whose type implements `Render`, and the model is accessible via this field.
|
||||
pub model: Model<V>,
|
||||
}
|
||||
|
||||
impl<V> Sealed for View<V> {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct AnyViewState {
|
||||
root_style: Style,
|
||||
cache_key: Option<ViewCacheKey>,
|
||||
@@ -58,6 +64,7 @@ impl<V: 'static> View<V> {
|
||||
Entity::downgrade(self)
|
||||
}
|
||||
|
||||
/// Update the view's state with the given function, which is passed a mutable reference and a context.
|
||||
pub fn update<C, R>(
|
||||
&self,
|
||||
cx: &mut C,
|
||||
@@ -69,10 +76,12 @@ impl<V: 'static> View<V> {
|
||||
cx.update_view(self, f)
|
||||
}
|
||||
|
||||
/// Obtain a read-only reference to this view's state.
|
||||
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
|
||||
self.model.read(cx)
|
||||
}
|
||||
|
||||
/// Gets a [FocusHandle] for this view when its state implements [FocusableView].
|
||||
pub fn focus_handle(&self, cx: &AppContext) -> FocusHandle
|
||||
where
|
||||
V: FocusableView,
|
||||
@@ -131,19 +140,24 @@ impl<V> PartialEq for View<V> {
|
||||
|
||||
impl<V> Eq for View<V> {}
|
||||
|
||||
/// A weak variant of [View] which does not prevent the view from being released.
|
||||
pub struct WeakView<V> {
|
||||
pub(crate) model: WeakModel<V>,
|
||||
}
|
||||
|
||||
impl<V: 'static> WeakView<V> {
|
||||
/// Gets the entity id associated with this handle.
|
||||
pub fn entity_id(&self) -> EntityId {
|
||||
self.model.entity_id
|
||||
}
|
||||
|
||||
/// Obtain a strong handle for the view if it hasn't been released.
|
||||
pub fn upgrade(&self) -> Option<View<V>> {
|
||||
Entity::upgrade_from(self)
|
||||
}
|
||||
|
||||
/// Update this view's state if it hasn't been released.
|
||||
/// Returns an error if this view has been released.
|
||||
pub fn update<C, R>(
|
||||
&self,
|
||||
cx: &mut C,
|
||||
@@ -157,9 +171,10 @@ impl<V: 'static> WeakView<V> {
|
||||
Ok(view.update(cx, f)).flatten()
|
||||
}
|
||||
|
||||
/// Assert that the view referenced by this handle has been released.
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn assert_dropped(&self) {
|
||||
self.model.assert_dropped()
|
||||
pub fn assert_released(&self) {
|
||||
self.model.assert_released()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,6 +200,7 @@ impl<V> PartialEq for WeakView<V> {
|
||||
|
||||
impl<V> Eq for WeakView<V> {}
|
||||
|
||||
/// A dynamically-typed handle to a view, which can be downcast to a [View] for a specific type.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnyView {
|
||||
model: AnyModel,
|
||||
@@ -193,11 +209,15 @@ pub struct AnyView {
|
||||
}
|
||||
|
||||
impl AnyView {
|
||||
/// Indicate that this view should be cached when using it as an element.
|
||||
/// When using this method, the view's previous layout and paint will be recycled from the previous frame if [ViewContext::notify] has not been called since it was rendered.
|
||||
/// The one exception is when [WindowContext::refresh] is called, in which case caching is ignored.
|
||||
pub fn cached(mut self) -> Self {
|
||||
self.cache = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Convert this to a weak handle.
|
||||
pub fn downgrade(&self) -> AnyWeakView {
|
||||
AnyWeakView {
|
||||
model: self.model.downgrade(),
|
||||
@@ -205,6 +225,8 @@ impl AnyView {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert this to a [View] of a specific type.
|
||||
/// If this handle does not contain a view of the specified type, returns itself in an `Err` variant.
|
||||
pub fn downcast<T: 'static>(self) -> Result<View<T>, Self> {
|
||||
match self.model.downcast() {
|
||||
Ok(model) => Ok(View { model }),
|
||||
@@ -216,10 +238,12 @@ impl AnyView {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the [TypeId] of the underlying view.
|
||||
pub fn entity_type(&self) -> TypeId {
|
||||
self.model.entity_type
|
||||
}
|
||||
|
||||
/// Gets the entity id of this handle.
|
||||
pub fn entity_id(&self) -> EntityId {
|
||||
self.model.entity_id()
|
||||
}
|
||||
@@ -337,12 +361,14 @@ impl IntoElement for AnyView {
|
||||
}
|
||||
}
|
||||
|
||||
/// A weak, dynamically-typed view handle that does not prevent the view from being released.
|
||||
pub struct AnyWeakView {
|
||||
model: AnyWeakModel,
|
||||
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, AnyElement),
|
||||
}
|
||||
|
||||
impl AnyWeakView {
|
||||
/// Convert to a strongly-typed handle if the referenced view has not yet been released.
|
||||
pub fn upgrade(&self) -> Option<AnyView> {
|
||||
let model = self.model.upgrade()?;
|
||||
Some(AnyView {
|
||||
|
||||
+61
-32
@@ -5,13 +5,14 @@ use crate::{
|
||||
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
|
||||
DevicePixels, DispatchActionListener, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect,
|
||||
Entity, EntityId, EventEmitter, FileDropEvent, Flatten, FontId, GlobalElementId, GlyphId, Hsla,
|
||||
ImageData, InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeystrokeEvent, LayoutId,
|
||||
Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseMoveEvent, MouseUpEvent,
|
||||
Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
|
||||
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
|
||||
RenderSvgParams, ScaledPixels, Scene, Shadow, SharedString, Size, Style, SubscriberSet,
|
||||
Subscription, Surface, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext,
|
||||
WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
|
||||
ImageData, IsZero, KeyBinding, KeyContext, KeyDownEvent, KeyEvent, KeystrokeEvent, LayoutId,
|
||||
Model, ModelContext, Modifiers, MonochromeSprite, MouseButton, MouseEvent, MouseMoveEvent,
|
||||
MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
||||
PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, PromptLevel, Quad, Render,
|
||||
RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, Scene, Shadow,
|
||||
SharedString, Size, Style, SubscriberSet, Subscription, Surface, TaffyLayoutEngine, Task,
|
||||
Underline, UnderlineStyle, View, VisualContext, WeakView, WindowBounds, WindowOptions,
|
||||
SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
@@ -29,7 +30,7 @@ use std::{
|
||||
borrow::{Borrow, BorrowMut, Cow},
|
||||
cell::RefCell,
|
||||
collections::hash_map::Entry,
|
||||
fmt::Debug,
|
||||
fmt::{Debug, Display},
|
||||
future::Future,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
@@ -315,6 +316,7 @@ pub(crate) struct Frame {
|
||||
pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
|
||||
pub(crate) z_index_stack: StackingOrder,
|
||||
pub(crate) next_stacking_order_id: u32,
|
||||
next_root_z_index: u8,
|
||||
content_mask_stack: Vec<ContentMask<Pixels>>,
|
||||
element_offset_stack: Vec<Point<Pixels>>,
|
||||
requested_input_handler: Option<RequestedInputHandler>,
|
||||
@@ -323,6 +325,9 @@ pub(crate) struct Frame {
|
||||
requested_cursor_style: Option<CursorStyle>,
|
||||
pub(crate) view_stack: Vec<EntityId>,
|
||||
pub(crate) reused_views: FxHashSet<EntityId>,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub(crate) debug_bounds: collections::FxHashMap<String, Bounds<Pixels>>,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
@@ -337,6 +342,7 @@ impl Frame {
|
||||
depth_map: Vec::new(),
|
||||
z_index_stack: StackingOrder::default(),
|
||||
next_stacking_order_id: 0,
|
||||
next_root_z_index: 0,
|
||||
content_mask_stack: Vec::new(),
|
||||
element_offset_stack: Vec::new(),
|
||||
requested_input_handler: None,
|
||||
@@ -345,6 +351,9 @@ impl Frame {
|
||||
requested_cursor_style: None,
|
||||
view_stack: Vec::new(),
|
||||
reused_views: FxHashSet::default(),
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
debug_bounds: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +363,7 @@ impl Frame {
|
||||
self.dispatch_tree.clear();
|
||||
self.depth_map.clear();
|
||||
self.next_stacking_order_id = 0;
|
||||
self.next_root_z_index = 0;
|
||||
self.reused_views.clear();
|
||||
self.scene.clear();
|
||||
self.requested_input_handler.take();
|
||||
@@ -968,7 +978,7 @@ impl<'a> WindowContext<'a> {
|
||||
/// Register a mouse event listener on the window for the next frame. The type of event
|
||||
/// is determined by the first parameter of the given listener. When the next frame is rendered
|
||||
/// the listener will be cleared.
|
||||
pub fn on_mouse_event<Event: 'static>(
|
||||
pub fn on_mouse_event<Event: MouseEvent>(
|
||||
&mut self,
|
||||
mut handler: impl FnMut(&Event, DispatchPhase, &mut WindowContext) + 'static,
|
||||
) {
|
||||
@@ -996,7 +1006,7 @@ impl<'a> WindowContext<'a> {
|
||||
///
|
||||
/// This is a fairly low-level method, so prefer using event handlers on elements unless you have
|
||||
/// a specific need to register a global listener.
|
||||
pub fn on_key_event<Event: 'static>(
|
||||
pub fn on_key_event<Event: KeyEvent>(
|
||||
&mut self,
|
||||
listener: impl Fn(&Event, DispatchPhase, &mut WindowContext) + 'static,
|
||||
) {
|
||||
@@ -1617,7 +1627,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
/// Dispatch a mouse or keyboard event on the window.
|
||||
pub fn dispatch_event(&mut self, event: InputEvent) -> bool {
|
||||
pub fn dispatch_event(&mut self, event: PlatformInput) -> bool {
|
||||
// Handlers may set this to false by calling `stop_propagation`.
|
||||
self.app.propagate_event = true;
|
||||
// Handlers may set this to true by calling `prevent_default`.
|
||||
@@ -1626,37 +1636,37 @@ impl<'a> WindowContext<'a> {
|
||||
let event = match event {
|
||||
// Track the mouse position with our own state, since accessing the platform
|
||||
// API for the mouse position can only occur on the main thread.
|
||||
InputEvent::MouseMove(mouse_move) => {
|
||||
PlatformInput::MouseMove(mouse_move) => {
|
||||
self.window.mouse_position = mouse_move.position;
|
||||
self.window.modifiers = mouse_move.modifiers;
|
||||
InputEvent::MouseMove(mouse_move)
|
||||
PlatformInput::MouseMove(mouse_move)
|
||||
}
|
||||
InputEvent::MouseDown(mouse_down) => {
|
||||
PlatformInput::MouseDown(mouse_down) => {
|
||||
self.window.mouse_position = mouse_down.position;
|
||||
self.window.modifiers = mouse_down.modifiers;
|
||||
InputEvent::MouseDown(mouse_down)
|
||||
PlatformInput::MouseDown(mouse_down)
|
||||
}
|
||||
InputEvent::MouseUp(mouse_up) => {
|
||||
PlatformInput::MouseUp(mouse_up) => {
|
||||
self.window.mouse_position = mouse_up.position;
|
||||
self.window.modifiers = mouse_up.modifiers;
|
||||
InputEvent::MouseUp(mouse_up)
|
||||
PlatformInput::MouseUp(mouse_up)
|
||||
}
|
||||
InputEvent::MouseExited(mouse_exited) => {
|
||||
PlatformInput::MouseExited(mouse_exited) => {
|
||||
self.window.modifiers = mouse_exited.modifiers;
|
||||
InputEvent::MouseExited(mouse_exited)
|
||||
PlatformInput::MouseExited(mouse_exited)
|
||||
}
|
||||
InputEvent::ModifiersChanged(modifiers_changed) => {
|
||||
PlatformInput::ModifiersChanged(modifiers_changed) => {
|
||||
self.window.modifiers = modifiers_changed.modifiers;
|
||||
InputEvent::ModifiersChanged(modifiers_changed)
|
||||
PlatformInput::ModifiersChanged(modifiers_changed)
|
||||
}
|
||||
InputEvent::ScrollWheel(scroll_wheel) => {
|
||||
PlatformInput::ScrollWheel(scroll_wheel) => {
|
||||
self.window.mouse_position = scroll_wheel.position;
|
||||
self.window.modifiers = scroll_wheel.modifiers;
|
||||
InputEvent::ScrollWheel(scroll_wheel)
|
||||
PlatformInput::ScrollWheel(scroll_wheel)
|
||||
}
|
||||
// Translate dragging and dropping of external files from the operating system
|
||||
// to internal drag and drop events.
|
||||
InputEvent::FileDrop(file_drop) => match file_drop {
|
||||
PlatformInput::FileDrop(file_drop) => match file_drop {
|
||||
FileDropEvent::Entered { position, paths } => {
|
||||
self.window.mouse_position = position;
|
||||
if self.active_drag.is_none() {
|
||||
@@ -1666,7 +1676,7 @@ impl<'a> WindowContext<'a> {
|
||||
cursor_offset: position,
|
||||
});
|
||||
}
|
||||
InputEvent::MouseMove(MouseMoveEvent {
|
||||
PlatformInput::MouseMove(MouseMoveEvent {
|
||||
position,
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
@@ -1674,7 +1684,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
FileDropEvent::Pending { position } => {
|
||||
self.window.mouse_position = position;
|
||||
InputEvent::MouseMove(MouseMoveEvent {
|
||||
PlatformInput::MouseMove(MouseMoveEvent {
|
||||
position,
|
||||
pressed_button: Some(MouseButton::Left),
|
||||
modifiers: Modifiers::default(),
|
||||
@@ -1683,21 +1693,21 @@ impl<'a> WindowContext<'a> {
|
||||
FileDropEvent::Submit { position } => {
|
||||
self.activate(true);
|
||||
self.window.mouse_position = position;
|
||||
InputEvent::MouseUp(MouseUpEvent {
|
||||
PlatformInput::MouseUp(MouseUpEvent {
|
||||
button: MouseButton::Left,
|
||||
position,
|
||||
modifiers: Modifiers::default(),
|
||||
click_count: 1,
|
||||
})
|
||||
}
|
||||
FileDropEvent::Exited => InputEvent::MouseUp(MouseUpEvent {
|
||||
FileDropEvent::Exited => PlatformInput::MouseUp(MouseUpEvent {
|
||||
button: MouseButton::Left,
|
||||
position: Point::default(),
|
||||
modifiers: Modifiers::default(),
|
||||
click_count: 1,
|
||||
}),
|
||||
},
|
||||
InputEvent::KeyDown(_) | InputEvent::KeyUp(_) => event,
|
||||
PlatformInput::KeyDown(_) | PlatformInput::KeyUp(_) => event,
|
||||
};
|
||||
|
||||
if let Some(any_mouse_event) = event.mouse_event() {
|
||||
@@ -2127,7 +2137,7 @@ impl<'a> WindowContext<'a> {
|
||||
.unwrap();
|
||||
|
||||
// Actual: Option<AnyElement> <- View
|
||||
// Requested: () <- AnyElemet
|
||||
// Requested: () <- AnyElement
|
||||
let state = state_box
|
||||
.take()
|
||||
.expect("element state is already on the stack");
|
||||
@@ -2450,8 +2460,13 @@ pub trait BorrowWindow: BorrowMut<Window> + BorrowMut<AppContext> {
|
||||
};
|
||||
let new_stacking_order_id =
|
||||
post_inc(&mut self.window_mut().next_frame.next_stacking_order_id);
|
||||
let new_root_z_index = post_inc(&mut self.window_mut().next_frame.next_root_z_index);
|
||||
let old_stacking_order = mem::take(&mut self.window_mut().next_frame.z_index_stack);
|
||||
self.window_mut().next_frame.z_index_stack.id = new_stacking_order_id;
|
||||
self.window_mut()
|
||||
.next_frame
|
||||
.z_index_stack
|
||||
.push(new_root_z_index);
|
||||
self.window_mut().next_frame.content_mask_stack.push(mask);
|
||||
let result = f(self);
|
||||
self.window_mut().next_frame.content_mask_stack.pop();
|
||||
@@ -2975,7 +2990,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||
/// Add a listener for any mouse event that occurs in the window.
|
||||
/// This is a fairly low level method.
|
||||
/// Typically, you'll want to use methods on UI elements, which perform bounds checking etc.
|
||||
pub fn on_mouse_event<Event: 'static>(
|
||||
pub fn on_mouse_event<Event: MouseEvent>(
|
||||
&mut self,
|
||||
handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + 'static,
|
||||
) {
|
||||
@@ -2988,7 +3003,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when the given Key Event is dispatched to the window.
|
||||
pub fn on_key_event<Event: 'static>(
|
||||
pub fn on_key_event<Event: KeyEvent>(
|
||||
&mut self,
|
||||
handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + 'static,
|
||||
) {
|
||||
@@ -3370,6 +3385,20 @@ pub enum ElementId {
|
||||
NamedInteger(SharedString, usize),
|
||||
}
|
||||
|
||||
impl Display for ElementId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ElementId::View(entity_id) => write!(f, "view-{}", entity_id)?,
|
||||
ElementId::Integer(ix) => write!(f, "{}", ix)?,
|
||||
ElementId::Name(name) => write!(f, "{}", name)?,
|
||||
ElementId::FocusHandle(__) => write!(f, "FocusHandle")?,
|
||||
ElementId::NamedInteger(s, i) => write!(f, "{}-{}", s, i)?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementId {
|
||||
pub(crate) fn from_entity_id(entity_id: EntityId) -> Self {
|
||||
ElementId::View(entity_id)
|
||||
|
||||
Reference in New Issue
Block a user