Make platform input handler private
Automatically record the context on non-view input handlers Simplify the async window context update() method
This commit is contained in:
@@ -213,7 +213,12 @@ impl AsyncWindowContext {
|
||||
}
|
||||
|
||||
/// A convenience method for [WindowContext::update()]
|
||||
pub fn update<R>(
|
||||
pub fn update<R>(&mut self, update: impl FnOnce(&mut WindowContext) -> R) -> Result<R> {
|
||||
self.app.update_window(self.window, |_, cx| update(cx))
|
||||
}
|
||||
|
||||
/// A convenience method for [WindowContext::update()]
|
||||
pub fn update_root<R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(AnyView, &mut WindowContext) -> R,
|
||||
) -> Result<R> {
|
||||
|
||||
@@ -1420,7 +1420,7 @@ impl Interactivity {
|
||||
|
||||
move |mut cx| async move {
|
||||
cx.background_executor().timer(TOOLTIP_DELAY).await;
|
||||
cx.update(|_, cx| {
|
||||
cx.update(|cx| {
|
||||
active_tooltip.borrow_mut().replace(
|
||||
ActiveTooltip {
|
||||
tooltip: Some(AnyTooltip {
|
||||
|
||||
+37
-45
@@ -1,13 +1,11 @@
|
||||
use crate::{
|
||||
AsyncWindowContext, Bounds, Pixels, PlatformInputHandler, View, ViewContext, WindowContext,
|
||||
};
|
||||
use crate::{Bounds, InputHandler, Pixels, View, ViewContext, WindowContext};
|
||||
use std::ops::Range;
|
||||
|
||||
/// Implement this trait to allow views to handle textual input when implementing an editor, field, etc.
|
||||
///
|
||||
/// Once your view `V` implements this trait, you can use it to construct an [`ElementInputHandler<V>`].
|
||||
/// This input handler can then be assigned during paint by calling [`WindowContext::handle_input`].
|
||||
pub trait InputHandler: 'static + Sized {
|
||||
pub trait ViewInputHandler: 'static + Sized {
|
||||
fn text_for_range(&mut self, range: Range<usize>, cx: &mut ViewContext<Self>)
|
||||
-> Option<String>;
|
||||
fn selected_text_range(&mut self, cx: &mut ViewContext<Self>) -> Option<Range<usize>>;
|
||||
@@ -39,7 +37,6 @@ pub trait InputHandler: 'static + Sized {
|
||||
pub struct ElementInputHandler<V> {
|
||||
view: View<V>,
|
||||
element_bounds: Bounds<Pixels>,
|
||||
cx: AsyncWindowContext,
|
||||
}
|
||||
|
||||
impl<V: 'static> ElementInputHandler<V> {
|
||||
@@ -47,45 +44,42 @@ impl<V: 'static> ElementInputHandler<V> {
|
||||
/// containing view.
|
||||
///
|
||||
/// [element_paint]: crate::Element::paint
|
||||
pub fn new(element_bounds: Bounds<Pixels>, view: View<V>, cx: &mut WindowContext) -> Self {
|
||||
pub fn new(element_bounds: Bounds<Pixels>, view: View<V>) -> Self {
|
||||
ElementInputHandler {
|
||||
view,
|
||||
element_bounds,
|
||||
cx: cx.to_async(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
|
||||
fn selected_text_range(&mut self) -> Option<Range<usize>> {
|
||||
impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
|
||||
fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
|
||||
self.view
|
||||
.update(&mut self.cx, |view, cx| view.selected_text_range(cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
.update(cx, |view, cx| view.selected_text_range(cx))
|
||||
}
|
||||
|
||||
fn marked_text_range(&mut self) -> Option<Range<usize>> {
|
||||
self.view
|
||||
.update(&mut self.cx, |view, cx| view.marked_text_range(cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
|
||||
self.view.update(cx, |view, cx| view.marked_text_range(cx))
|
||||
}
|
||||
|
||||
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
|
||||
fn text_for_range(
|
||||
&mut self,
|
||||
range_utf16: Range<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<String> {
|
||||
self.view
|
||||
.update(&mut self.cx, |view, cx| {
|
||||
view.text_for_range(range_utf16, cx)
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
.update(cx, |view, cx| view.text_for_range(range_utf16, cx))
|
||||
}
|
||||
|
||||
fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
|
||||
self.view
|
||||
.update(&mut self.cx, |view, cx| {
|
||||
view.replace_text_in_range(replacement_range, text, cx)
|
||||
})
|
||||
.ok();
|
||||
fn replace_text_in_range(
|
||||
&mut self,
|
||||
replacement_range: Option<Range<usize>>,
|
||||
text: &str,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
self.view.update(cx, |view, cx| {
|
||||
view.replace_text_in_range(replacement_range, text, cx)
|
||||
});
|
||||
}
|
||||
|
||||
fn replace_and_mark_text_in_range(
|
||||
@@ -93,26 +87,24 @@ impl<V: InputHandler> PlatformInputHandler for ElementInputHandler<V> {
|
||||
range_utf16: Option<Range<usize>>,
|
||||
new_text: &str,
|
||||
new_selected_range: Option<Range<usize>>,
|
||||
cx: &mut WindowContext,
|
||||
) {
|
||||
self.view
|
||||
.update(&mut self.cx, |view, cx| {
|
||||
view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
|
||||
})
|
||||
.ok();
|
||||
self.view.update(cx, |view, cx| {
|
||||
view.replace_and_mark_text_in_range(range_utf16, new_text, new_selected_range, cx)
|
||||
});
|
||||
}
|
||||
|
||||
fn unmark_text(&mut self) {
|
||||
self.view
|
||||
.update(&mut self.cx, |view, cx| view.unmark_text(cx))
|
||||
.ok();
|
||||
fn unmark_text(&mut self, cx: &mut WindowContext) {
|
||||
self.view.update(cx, |view, cx| view.unmark_text(cx));
|
||||
}
|
||||
|
||||
fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
|
||||
self.view
|
||||
.update(&mut self.cx, |view, cx| {
|
||||
view.bounds_for_range(range_utf16, self.element_bounds, cx)
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
fn bounds_for_range(
|
||||
&mut self,
|
||||
range_utf16: Range<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<Bounds<Pixels>> {
|
||||
self.view.update(cx, |view, cx| {
|
||||
view.bounds_for_range(range_utf16, self.element_bounds, cx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+97
-13
@@ -6,10 +6,10 @@ mod mac;
|
||||
mod test;
|
||||
|
||||
use crate::{
|
||||
Action, AnyWindowHandle, BackgroundExecutor, Bounds, DevicePixels, Font, FontId, FontMetrics,
|
||||
FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout, Pixels, PlatformInput,
|
||||
Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result, Scene, SharedString,
|
||||
Size, TaskLabel,
|
||||
Action, AnyWindowHandle, AsyncWindowContext, BackgroundExecutor, Bounds, DevicePixels, Font,
|
||||
FontId, FontMetrics, FontRun, ForegroundExecutor, GlobalPixels, GlyphId, Keymap, LineLayout,
|
||||
Pixels, PlatformInput, Point, RenderGlyphParams, RenderImageParams, RenderSvgParams, Result,
|
||||
Scene, SharedString, Size, TaskLabel, WindowContext,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use async_task::Runnable;
|
||||
@@ -149,8 +149,8 @@ pub(crate) trait PlatformWindow {
|
||||
fn mouse_position(&self) -> Point<Pixels>;
|
||||
fn modifiers(&self) -> Modifiers;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>);
|
||||
fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>>;
|
||||
fn set_input_handler(&mut self, input_handler: PlatformInputHandler);
|
||||
fn take_input_handler(&mut self) -> Option<PlatformInputHandler>;
|
||||
fn prompt(&self, level: PromptLevel, msg: &str, answers: &[&str]) -> oneshot::Receiver<usize>;
|
||||
fn activate(&self);
|
||||
fn set_title(&mut self, title: &str);
|
||||
@@ -325,19 +325,103 @@ impl From<TileId> for etagere::AllocId {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PlatformInputHandler: 'static {
|
||||
fn selected_text_range(&mut self) -> Option<Range<usize>>;
|
||||
fn marked_text_range(&mut self) -> Option<Range<usize>>;
|
||||
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String>;
|
||||
fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str);
|
||||
pub(crate) struct PlatformInputHandler {
|
||||
cx: AsyncWindowContext,
|
||||
handler: Box<dyn InputHandler>,
|
||||
}
|
||||
|
||||
impl PlatformInputHandler {
|
||||
pub fn new(cx: AsyncWindowContext, handler: Box<dyn InputHandler>) -> Self {
|
||||
Self { cx, handler }
|
||||
}
|
||||
|
||||
fn selected_text_range(&mut self) -> Option<Range<usize>> {
|
||||
self.cx
|
||||
.update(|cx| self.handler.selected_text_range(cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn marked_text_range(&mut self) -> Option<Range<usize>> {
|
||||
self.cx
|
||||
.update(|cx| self.handler.marked_text_range(cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn text_for_range(&mut self, range_utf16: Range<usize>) -> Option<String> {
|
||||
self.cx
|
||||
.update(|cx| self.handler.text_for_range(range_utf16, cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
fn replace_text_in_range(&mut self, replacement_range: Option<Range<usize>>, text: &str) {
|
||||
self.cx
|
||||
.update(|cx| {
|
||||
self.handler
|
||||
.replace_text_in_range(replacement_range, text, cx)
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn replace_and_mark_text_in_range(
|
||||
&mut self,
|
||||
range_utf16: Option<Range<usize>>,
|
||||
new_text: &str,
|
||||
new_selected_range: Option<Range<usize>>,
|
||||
) {
|
||||
self.cx
|
||||
.update(|cx| {
|
||||
self.handler.replace_and_mark_text_in_range(
|
||||
range_utf16,
|
||||
new_text,
|
||||
new_selected_range,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
fn unmark_text(&mut self) {
|
||||
self.cx.update(|cx| self.handler.unmark_text(cx)).ok();
|
||||
}
|
||||
|
||||
fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>> {
|
||||
self.cx
|
||||
.update(|cx| self.handler.bounds_for_range(range_utf16, cx))
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait InputHandler: 'static {
|
||||
fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
|
||||
fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
|
||||
fn text_for_range(
|
||||
&mut self,
|
||||
range_utf16: Range<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<String>;
|
||||
fn replace_text_in_range(
|
||||
&mut self,
|
||||
replacement_range: Option<Range<usize>>,
|
||||
text: &str,
|
||||
cx: &mut WindowContext,
|
||||
);
|
||||
fn unmark_text(&mut self);
|
||||
fn bounds_for_range(&mut self, range_utf16: Range<usize>) -> Option<Bounds<Pixels>>;
|
||||
fn replace_and_mark_text_in_range(
|
||||
&mut self,
|
||||
range_utf16: Option<Range<usize>>,
|
||||
new_text: &str,
|
||||
new_selected_range: Option<Range<usize>>,
|
||||
cx: &mut WindowContext,
|
||||
);
|
||||
fn unmark_text(&mut self, cx: &mut WindowContext);
|
||||
fn bounds_for_range(
|
||||
&mut self,
|
||||
range_utf16: Range<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<Bounds<Pixels>>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::{global_bounds_from_ns_rect, ns_string, MacDisplay, MetalRenderer, NSRange};
|
||||
use crate::{
|
||||
global_bounds_to_ns_rect, point, px, size, AnyWindowHandle, Bounds, ExternalPaths,
|
||||
FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent, Keystroke, Modifiers,
|
||||
ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point,
|
||||
global_bounds_to_ns_rect, platform::PlatformInputHandler, point, px, size, AnyWindowHandle,
|
||||
Bounds, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels, KeyDownEvent,
|
||||
Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent,
|
||||
MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
|
||||
PromptLevel, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
|
||||
};
|
||||
use block::ConcreteBlock;
|
||||
@@ -327,7 +327,7 @@ struct MacWindowState {
|
||||
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
|
||||
close_callback: Option<Box<dyn FnOnce()>>,
|
||||
appearance_changed_callback: Option<Box<dyn FnMut()>>,
|
||||
input_handler: Option<Box<dyn PlatformInputHandler>>,
|
||||
input_handler: Option<PlatformInputHandler>,
|
||||
pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
|
||||
last_key_equivalent: Option<KeyDownEvent>,
|
||||
synthetic_drag_counter: usize,
|
||||
@@ -764,11 +764,11 @@ impl PlatformWindow for MacWindow {
|
||||
self
|
||||
}
|
||||
|
||||
fn set_input_handler(&mut self, input_handler: Box<dyn PlatformInputHandler>) {
|
||||
fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {
|
||||
self.0.as_ref().lock().input_handler = Some(input_handler);
|
||||
}
|
||||
|
||||
fn take_input_handler(&mut self) -> Option<Box<dyn PlatformInputHandler>> {
|
||||
fn take_input_handler(&mut self) -> Option<PlatformInputHandler> {
|
||||
self.0.as_ref().lock().input_handler.take()
|
||||
}
|
||||
|
||||
@@ -1761,13 +1761,13 @@ fn drag_event_position(window_state: &Mutex<MacWindowState>, dragging_info: id)
|
||||
|
||||
fn with_input_handler<F, R>(window: &Object, f: F) -> Option<R>
|
||||
where
|
||||
F: FnOnce(&mut dyn PlatformInputHandler) -> R,
|
||||
F: FnOnce(&mut PlatformInputHandler) -> R,
|
||||
{
|
||||
let window_state = unsafe { get_window_state(window) };
|
||||
let mut lock = window_state.as_ref().lock();
|
||||
if let Some(mut input_handler) = lock.input_handler.take() {
|
||||
drop(lock);
|
||||
let result = f(input_handler.as_mut());
|
||||
let result = f(&mut input_handler);
|
||||
window_state.lock().input_handler = Some(input_handler);
|
||||
Some(result)
|
||||
} else {
|
||||
|
||||
+14
-15
@@ -5,13 +5,13 @@ 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, 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,
|
||||
ImageData, InputHandler, 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};
|
||||
@@ -298,7 +298,7 @@ pub(crate) struct ElementStateBox {
|
||||
|
||||
struct RequestedInputHandler {
|
||||
view_id: EntityId,
|
||||
handler: Option<Box<dyn PlatformInputHandler>>,
|
||||
handler: Option<PlatformInputHandler>,
|
||||
}
|
||||
|
||||
struct TooltipRequest {
|
||||
@@ -2188,16 +2188,15 @@ impl<'a> WindowContext<'a> {
|
||||
/// rendered.
|
||||
///
|
||||
/// [element_input_handler]: crate::ElementInputHandler
|
||||
pub fn handle_input(
|
||||
&mut self,
|
||||
focus_handle: &FocusHandle,
|
||||
input_handler: impl PlatformInputHandler,
|
||||
) {
|
||||
pub fn handle_input(&mut self, focus_handle: &FocusHandle, input_handler: impl InputHandler) {
|
||||
if focus_handle.is_focused(self) {
|
||||
let view_id = self.parent_view_id();
|
||||
self.window.next_frame.requested_input_handler = Some(RequestedInputHandler {
|
||||
view_id,
|
||||
handler: Some(Box::new(input_handler)),
|
||||
handler: Some(PlatformInputHandler::new(
|
||||
self.to_async(),
|
||||
Box::new(input_handler),
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2209,7 +2208,7 @@ impl<'a> WindowContext<'a> {
|
||||
self.window
|
||||
.platform_window
|
||||
.on_should_close(Box::new(move || {
|
||||
this.update(|_, cx| {
|
||||
this.update(|cx| {
|
||||
// Ensure that the window is removed from the app if it's been closed
|
||||
// by always pre-empting the system close event.
|
||||
if f(cx) {
|
||||
|
||||
Reference in New Issue
Block a user