Call methods on the focused view during input events

This commit is contained in:
Max Brunsfeld 2022-07-20 16:44:26 -07:00
parent 1b0e93b153
commit 0b81a4dfae
3 changed files with 281 additions and 38 deletions

View file

@ -7,8 +7,8 @@ use crate::{
platform::{self, KeyDownEvent, Platform, PromptLevel, WindowOptions},
presenter::Presenter,
util::post_inc,
AssetCache, AssetSource, ClipboardItem, FontCache, MouseRegionId, PathPromptOptions,
TextLayoutCache,
AssetCache, AssetSource, ClipboardItem, FontCache, InputHandler, MouseRegionId,
PathPromptOptions, TextLayoutCache,
};
pub use action::*;
use anyhow::{anyhow, Context, Result};
@ -28,7 +28,7 @@ use std::{
hash::{Hash, Hasher},
marker::PhantomData,
mem,
ops::{Deref, DerefMut},
ops::{Deref, DerefMut, Range},
path::{Path, PathBuf},
pin::Pin,
rc::{self, Rc},
@ -64,6 +64,33 @@ pub trait View: Entity + Sized {
fn debug_json(&self, _: &AppContext) -> serde_json::Value {
serde_json::Value::Null
}
fn text_for_range(&self, _: Range<usize>, _: &AppContext) -> Option<String> {
None
}
fn selected_text_range(&self, _: &AppContext) -> Option<Range<usize>> {
None
}
fn set_selected_text_range(&mut self, _: Range<usize>, _: &mut ViewContext<Self>) {}
fn marked_text_range(&self, _: &AppContext) -> Option<Range<usize>> {
None
}
fn unmark_text(&mut self, _: &mut ViewContext<Self>) {}
fn replace_text_in_range(
&mut self,
_: Option<Range<usize>>,
_: &str,
_: &mut ViewContext<Self>,
) {
}
fn replace_and_mark_text_in_range(
&mut self,
_: Option<Range<usize>>,
_: &str,
_: Option<Range<usize>>,
_: &mut ViewContext<Self>,
) {
}
}
pub trait ReadModel {
@ -154,6 +181,11 @@ pub struct TestAppContext {
condition_duration: Option<Duration>,
}
pub struct WindowInputHandler {
app: Rc<RefCell<MutableAppContext>>,
window_id: usize,
}
impl App {
pub fn new(asset_source: impl AssetSource) -> Result<Self> {
let platform = platform::current::platform();
@ -310,6 +342,121 @@ impl App {
}
}
impl WindowInputHandler {
fn read_focused_view<T, F>(&self, f: F) -> Option<T>
where
F: FnOnce(&dyn AnyView, &AppContext) -> T,
{
let app = self.app.borrow();
let view_id = app.focused_view_id(self.window_id)?;
let view = app.cx.views.get(&(self.window_id, view_id))?;
let result = f(view.as_ref(), &app);
Some(result)
}
fn update_focused_view<T, F>(&mut self, f: F) -> Option<T>
where
F: FnOnce(usize, usize, &mut dyn AnyView, &mut MutableAppContext) -> T,
{
let mut app = self.app.borrow_mut();
app.update(|app| {
let view_id = app.focused_view_id(self.window_id)?;
let mut view = app.cx.views.remove(&(self.window_id, view_id))?;
let result = f(self.window_id, view_id, view.as_mut(), &mut *app);
app.cx.views.insert((self.window_id, view_id), view);
Some(result)
})
}
}
impl InputHandler for WindowInputHandler {
fn text_for_range(&self, range: Range<usize>) -> Option<String> {
let result = self
.read_focused_view(|view, cx| view.text_for_range(range.clone(), cx))
.flatten();
eprintln!("text_for_range({range:?}) -> {result:?}");
result
}
fn selected_text_range(&self) -> Option<Range<usize>> {
let result = self
.read_focused_view(|view, cx| view.selected_text_range(cx))
.flatten();
eprintln!("selected_text_range() -> {result:?}");
result
}
fn set_selected_text_range(&mut self, range: Range<usize>) {
eprintln!("set_selected_text_range({range:?})");
self.update_focused_view(|window_id, view_id, view, cx| {
view.set_selected_text_range(range, cx, window_id, view_id);
});
}
fn replace_text_in_range(&mut self, range: Option<Range<usize>>, text: &str) {
eprintln!("replace_text_in_range({range:?}, {text:?})");
self.update_focused_view(|window_id, view_id, view, cx| {
view.replace_text_in_range(range, text, cx, window_id, view_id);
});
}
fn marked_text_range(&self) -> Option<Range<usize>> {
let result = self
.read_focused_view(|view, cx| view.marked_text_range(cx))
.flatten();
eprintln!("marked_text_range() -> {result:?}");
result
}
fn unmark_text(&mut self) {
eprintln!("unmark_text()");
self.update_focused_view(|window_id, view_id, view, cx| {
view.unmark_text(cx, window_id, view_id);
});
}
fn replace_and_mark_text_in_range(
&mut self,
range: Option<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
) {
eprintln!(
"replace_and_mark_text_in_range({range:?}, {new_text:?}, {new_selected_range:?})"
);
self.update_focused_view(|window_id, view_id, view, cx| {
view.replace_and_mark_text_in_range(
range,
new_text,
new_selected_range,
cx,
window_id,
view_id,
);
});
}
// TODO - do these need to be handled separately?
fn cancel_composition(&mut self) {
self.unmark_text();
}
fn finish_composition(&mut self) {
self.unmark_text();
}
}
#[cfg(any(test, feature = "test-support"))]
impl TestAppContext {
pub fn new(
@ -1888,6 +2035,11 @@ impl MutableAppContext {
}));
}
window.set_input_handler(Box::new(WindowInputHandler {
app: self.upgrade().0,
window_id,
}));
let scene =
presenter
.borrow_mut()
@ -3179,6 +3331,35 @@ pub trait AnyView {
fn on_blur(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize);
fn keymap_context(&self, cx: &AppContext) -> keymap::Context;
fn debug_json(&self, cx: &AppContext) -> serde_json::Value;
fn text_for_range(&self, range: Range<usize>, cx: &AppContext) -> Option<String>;
fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>>;
fn set_selected_text_range(
&mut self,
range: Range<usize>,
cx: &mut MutableAppContext,
window_id: usize,
view_id: usize,
);
fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>>;
fn unmark_text(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize);
fn replace_text_in_range(
&mut self,
range: Option<Range<usize>>,
text: &str,
cx: &mut MutableAppContext,
window_id: usize,
view_id: usize,
);
fn replace_and_mark_text_in_range(
&mut self,
range: Option<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
cx: &mut MutableAppContext,
window_id: usize,
view_id: usize,
);
}
impl<T> AnyView for T
@ -3229,6 +3410,59 @@ where
fn debug_json(&self, cx: &AppContext) -> serde_json::Value {
View::debug_json(self, cx)
}
fn text_for_range(&self, range: Range<usize>, cx: &AppContext) -> Option<String> {
View::text_for_range(self, range, cx)
}
fn selected_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
View::selected_text_range(self, cx)
}
fn set_selected_text_range(
&mut self,
range: Range<usize>,
cx: &mut MutableAppContext,
window_id: usize,
view_id: usize,
) {
let mut cx = ViewContext::new(cx, window_id, view_id);
View::set_selected_text_range(self, range, &mut cx)
}
fn marked_text_range(&self, cx: &AppContext) -> Option<Range<usize>> {
View::marked_text_range(self, cx)
}
fn unmark_text(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize) {
let mut cx = ViewContext::new(cx, window_id, view_id);
View::unmark_text(self, &mut cx)
}
fn replace_text_in_range(
&mut self,
range: Option<Range<usize>>,
text: &str,
cx: &mut MutableAppContext,
window_id: usize,
view_id: usize,
) {
let mut cx = ViewContext::new(cx, window_id, view_id);
View::replace_text_in_range(self, range, text, &mut cx)
}
fn replace_and_mark_text_in_range(
&mut self,
range: Option<Range<usize>>,
new_text: &str,
new_selected_range: Option<Range<usize>>,
cx: &mut MutableAppContext,
window_id: usize,
view_id: usize,
) {
let mut cx = ViewContext::new(cx, window_id, view_id);
View::replace_and_mark_text_in_range(self, range, new_text, new_selected_range, &mut cx)
}
}
pub struct ModelContext<'a, T: ?Sized> {