Flesh out gpui2 test support
This commit is contained in:
parent
eb325fb387
commit
8e799b6e22
7 changed files with 271 additions and 13 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, BackgroundExecutor, Context,
|
||||
EventEmitter, ForegroundExecutor, Model, ModelContext, Result, Task, TestDispatcher,
|
||||
TestPlatform, WindowContext,
|
||||
EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model, ModelContext,
|
||||
Result, Task, TestDispatcher, TestPlatform, WindowContext,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
use futures::{Stream, StreamExt};
|
||||
|
@ -129,6 +129,23 @@ impl TestAppContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn dispatch_keystroke(
|
||||
&mut self,
|
||||
window: AnyWindowHandle,
|
||||
keystroke: Keystroke,
|
||||
is_held: bool,
|
||||
) {
|
||||
let handled = window
|
||||
.update(self, |_, cx| {
|
||||
cx.dispatch_event(InputEvent::KeyDown(KeyDownEvent { keystroke, is_held }))
|
||||
})
|
||||
.is_ok_and(|handled| handled);
|
||||
|
||||
if !handled {
|
||||
// todo!() simluate input here
|
||||
}
|
||||
}
|
||||
|
||||
pub fn notifications<T: 'static>(&mut self, entity: &Model<T>) -> impl Stream<Item = ()> {
|
||||
let (tx, rx) = futures::channel::mpsc::unbounded();
|
||||
|
||||
|
|
|
@ -25,6 +25,10 @@ impl<T: Clone + Debug + Default> Point<T> {
|
|||
Self { x, y }
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self::new(T::default(), T::default())
|
||||
}
|
||||
|
||||
pub fn map<U: Clone + Default + Debug>(&self, f: impl Fn(T) -> U) -> Point<U> {
|
||||
Point {
|
||||
x: f(self.x.clone()),
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
mod dispatcher;
|
||||
mod display;
|
||||
mod platform;
|
||||
mod window;
|
||||
|
||||
pub use dispatcher::*;
|
||||
pub use display::*;
|
||||
pub use platform::*;
|
||||
pub use window::*;
|
||||
|
|
41
crates/gpui2/src/platform/test/display.rs
Normal file
41
crates/gpui2/src/platform/test/display.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use anyhow::{Ok, Result};
|
||||
|
||||
use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Point};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestDisplay {
|
||||
id: DisplayId,
|
||||
uuid: uuid::Uuid,
|
||||
bounds: Bounds<GlobalPixels>,
|
||||
}
|
||||
|
||||
impl TestDisplay {
|
||||
pub fn new() -> Self {
|
||||
TestDisplay {
|
||||
id: DisplayId(1),
|
||||
uuid: uuid::Uuid::new_v4(),
|
||||
bounds: Bounds::from_corners(
|
||||
Point::zero(),
|
||||
Point::new(GlobalPixels(1920.), GlobalPixels(1080.)),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformDisplay for TestDisplay {
|
||||
fn id(&self) -> crate::DisplayId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn uuid(&self) -> Result<uuid::Uuid> {
|
||||
Ok(self.uuid)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn bounds(&self) -> crate::Bounds<crate::GlobalPixels> {
|
||||
self.bounds
|
||||
}
|
||||
}
|
|
@ -1,10 +1,18 @@
|
|||
use crate::{BackgroundExecutor, DisplayId, ForegroundExecutor, Platform, PlatformTextSystem};
|
||||
use crate::{
|
||||
AnyWindowHandle, BackgroundExecutor, CursorStyle, DisplayId, ForegroundExecutor, Platform,
|
||||
PlatformDisplay, PlatformTextSystem, TestDisplay, TestWindow, WindowOptions,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use std::sync::Arc;
|
||||
use parking_lot::Mutex;
|
||||
use std::{rc::Rc, sync::Arc};
|
||||
|
||||
pub struct TestPlatform {
|
||||
background_executor: BackgroundExecutor,
|
||||
foreground_executor: ForegroundExecutor,
|
||||
|
||||
active_window: Arc<Mutex<Option<AnyWindowHandle>>>,
|
||||
active_display: Rc<dyn PlatformDisplay>,
|
||||
active_cursor: Mutex<CursorStyle>,
|
||||
}
|
||||
|
||||
impl TestPlatform {
|
||||
|
@ -12,6 +20,10 @@ impl TestPlatform {
|
|||
TestPlatform {
|
||||
background_executor: executor,
|
||||
foreground_executor,
|
||||
|
||||
active_cursor: Default::default(),
|
||||
active_display: Rc::new(TestDisplay::new()),
|
||||
active_window: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,11 +71,11 @@ impl Platform for TestPlatform {
|
|||
}
|
||||
|
||||
fn displays(&self) -> Vec<std::rc::Rc<dyn crate::PlatformDisplay>> {
|
||||
unimplemented!()
|
||||
vec![self.active_display.clone()]
|
||||
}
|
||||
|
||||
fn display(&self, _id: DisplayId) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
|
||||
unimplemented!()
|
||||
fn display(&self, id: DisplayId) -> Option<std::rc::Rc<dyn crate::PlatformDisplay>> {
|
||||
self.displays().iter().find(|d| d.id() == id).cloned()
|
||||
}
|
||||
|
||||
fn main_window(&self) -> Option<crate::AnyWindowHandle> {
|
||||
|
@ -72,10 +84,11 @@ impl Platform for TestPlatform {
|
|||
|
||||
fn open_window(
|
||||
&self,
|
||||
_handle: crate::AnyWindowHandle,
|
||||
_options: crate::WindowOptions,
|
||||
handle: AnyWindowHandle,
|
||||
options: WindowOptions,
|
||||
) -> Box<dyn crate::PlatformWindow> {
|
||||
unimplemented!()
|
||||
*self.active_window.lock() = Some(handle);
|
||||
Box::new(TestWindow::new(options, self.active_display.clone()))
|
||||
}
|
||||
|
||||
fn set_display_link_output_callback(
|
||||
|
@ -164,8 +177,8 @@ impl Platform for TestPlatform {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn set_cursor_style(&self, _style: crate::CursorStyle) {
|
||||
unimplemented!()
|
||||
fn set_cursor_style(&self, style: crate::CursorStyle) {
|
||||
*self.active_cursor.lock() = style;
|
||||
}
|
||||
|
||||
fn should_auto_hide_scrollbars(&self) -> bool {
|
||||
|
|
179
crates/gpui2/src/platform/test/window.rs
Normal file
179
crates/gpui2/src/platform/test/window.rs
Normal file
|
@ -0,0 +1,179 @@
|
|||
use std::{rc::Rc, sync::Arc};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
use crate::{
|
||||
px, Pixels, PlatformAtlas, PlatformDisplay, PlatformWindow, Point, Scene, Size,
|
||||
WindowAppearance, WindowBounds, WindowOptions,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
struct Handlers {
|
||||
active_status_change: Vec<Box<dyn FnMut(bool)>>,
|
||||
input: Vec<Box<dyn FnMut(crate::InputEvent) -> bool>>,
|
||||
moved: Vec<Box<dyn FnMut()>>,
|
||||
resize: Vec<Box<dyn FnMut(Size<Pixels>, f32)>>,
|
||||
}
|
||||
|
||||
pub struct TestWindow {
|
||||
bounds: WindowBounds,
|
||||
current_scene: Mutex<Option<Scene>>,
|
||||
display: Rc<dyn PlatformDisplay>,
|
||||
|
||||
handlers: Mutex<Handlers>,
|
||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||
}
|
||||
impl TestWindow {
|
||||
pub fn new(options: WindowOptions, display: Rc<dyn PlatformDisplay>) -> Self {
|
||||
Self {
|
||||
bounds: options.bounds,
|
||||
current_scene: Default::default(),
|
||||
display,
|
||||
|
||||
sprite_atlas: Arc::new(TestAtlas),
|
||||
handlers: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PlatformWindow for TestWindow {
|
||||
fn bounds(&self) -> WindowBounds {
|
||||
self.bounds
|
||||
}
|
||||
|
||||
fn content_size(&self) -> Size<Pixels> {
|
||||
let bounds = match self.bounds {
|
||||
WindowBounds::Fixed(bounds) => bounds,
|
||||
WindowBounds::Maximized | WindowBounds::Fullscreen => self.display().bounds(),
|
||||
};
|
||||
bounds.size.map(|p| px(p.0))
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f32 {
|
||||
2.0
|
||||
}
|
||||
|
||||
fn titlebar_height(&self) -> Pixels {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn appearance(&self) -> WindowAppearance {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn display(&self) -> std::rc::Rc<dyn crate::PlatformDisplay> {
|
||||
self.display.clone()
|
||||
}
|
||||
|
||||
fn mouse_position(&self) -> Point<Pixels> {
|
||||
Point::zero()
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_input_handler(&mut self, _input_handler: Box<dyn crate::PlatformInputHandler>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
&self,
|
||||
_level: crate::PromptLevel,
|
||||
_msg: &str,
|
||||
_answers: &[&str],
|
||||
) -> futures::channel::oneshot::Receiver<usize> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn activate(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_title(&mut self, _title: &str) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_edited(&mut self, _edited: bool) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn show_character_palette(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn minimize(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn zoom(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn toggle_full_screen(&self) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn on_input(&self, callback: Box<dyn FnMut(crate::InputEvent) -> bool>) {
|
||||
self.handlers.lock().input.push(callback)
|
||||
}
|
||||
|
||||
fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
|
||||
self.handlers.lock().active_status_change.push(callback)
|
||||
}
|
||||
|
||||
fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
|
||||
self.handlers.lock().resize.push(callback)
|
||||
}
|
||||
|
||||
fn on_fullscreen(&self, _callback: Box<dyn FnMut(bool)>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn on_moved(&self, callback: Box<dyn FnMut()>) {
|
||||
self.handlers.lock().moved.push(callback)
|
||||
}
|
||||
|
||||
fn on_should_close(&self, _callback: Box<dyn FnMut() -> bool>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn on_close(&self, _callback: Box<dyn FnOnce()>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn on_appearance_changed(&self, _callback: Box<dyn FnMut()>) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn is_topmost_for_position(&self, _position: crate::Point<Pixels>) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn draw(&self, scene: crate::Scene) {
|
||||
self.current_scene.lock().replace(scene);
|
||||
}
|
||||
|
||||
fn sprite_atlas(&self) -> std::sync::Arc<dyn crate::PlatformAtlas> {
|
||||
self.sprite_atlas.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestAtlas;
|
||||
|
||||
impl PlatformAtlas for TestAtlas {
|
||||
fn get_or_insert_with<'a>(
|
||||
&self,
|
||||
_key: &crate::AtlasKey,
|
||||
_build: &mut dyn FnMut() -> anyhow::Result<(
|
||||
Size<crate::DevicePixels>,
|
||||
std::borrow::Cow<'a, [u8]>,
|
||||
)>,
|
||||
) -> anyhow::Result<crate::AtlasTile> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn clear(&self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -1047,7 +1047,7 @@ impl<'a> WindowContext<'a> {
|
|||
}
|
||||
|
||||
/// Dispatch a mouse or keyboard event on the window.
|
||||
fn dispatch_event(&mut self, event: InputEvent) -> bool {
|
||||
pub fn dispatch_event(&mut self, event: InputEvent) -> bool {
|
||||
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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue