Something went wrong. Try again.
we (web engine): Experimental web browser project to understand the limits of Claude
Something went wrong. Try again.
Rust
at url-parser
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790//! AppKit FFI bindings for macOS window creation.//!//! Provides wrappers around NSApplication, NSWindow, NSAutoreleasePool, and//! NSView for opening native macOS windows.//!//! # Safety//!//! This module contains `unsafe` code for FFI with AppKit.//! The `platform` crate is one of the few crates where `unsafe` is permitted.
use crate::cf::CfString;use crate::cg::{self, BitmapContext, CGRect};use crate::objc::{Class, Id, Imp, Sel};use crate::{class, msg_send};use std::ffi::CStr;use std::os::raw::{c_char, c_void};
// ---------------------------------------------------------------------------// AppKit framework link// ---------------------------------------------------------------------------
#[link(name = "AppKit", kind = "framework")]extern "C" {}
// ---------------------------------------------------------------------------// Geometry types matching AppKit's expectations// ---------------------------------------------------------------------------
/// `NSRect` / `CGRect` — a rectangle defined by origin and size.#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct NSRect { pub origin: NSPoint, pub size: NSSize,}
/// `NSPoint` / `CGPoint` — a point in 2D space.#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct NSPoint { pub x: f64, pub y: f64,}
/// `NSSize` / `CGSize` — a 2D size.#[repr(C)]#[derive(Debug, Clone, Copy)]pub struct NSSize { pub width: f64, pub height: f64,}
impl NSRect { /// Create a new rectangle. pub fn new(x: f64, y: f64, width: f64, height: f64) -> NSRect { NSRect { origin: NSPoint { x, y }, size: NSSize { width, height }, } }}
// ---------------------------------------------------------------------------// NSWindow style mask constants// ---------------------------------------------------------------------------
/// Window has a title bar.pub const NS_WINDOW_STYLE_MASK_TITLED: u64 = 1 << 0;/// Window has a close button.pub const NS_WINDOW_STYLE_MASK_CLOSABLE: u64 = 1 << 1;/// Window can be minimized.pub const NS_WINDOW_STYLE_MASK_MINIATURIZABLE: u64 = 1 << 2;/// Window can be resized.pub const NS_WINDOW_STYLE_MASK_RESIZABLE: u64 = 1 << 3;
// ---------------------------------------------------------------------------// NSBackingStoreType constants// ---------------------------------------------------------------------------
/// Buffered backing store (the standard for modern macOS).pub const NS_BACKING_STORE_BUFFERED: u64 = 2;
// ---------------------------------------------------------------------------// NSApplicationActivationPolicy constants// ---------------------------------------------------------------------------
/// Regular application that appears in the Dock and may have a menu bar.pub const NS_APPLICATION_ACTIVATION_POLICY_REGULAR: i64 = 0;
// ---------------------------------------------------------------------------// NSAutoreleasePool// ---------------------------------------------------------------------------
/// RAII wrapper for `NSAutoreleasePool`.////// Creates a pool on construction and drains it on drop. Required for any/// Objective-C code that creates autoreleased objects.pub struct AutoreleasePool { pool: Id,}
impl Default for AutoreleasePool { fn default() -> Self { Self::new() }}
impl AutoreleasePool { /// Create a new autorelease pool. pub fn new() -> AutoreleasePool { let cls = class!("NSAutoreleasePool").expect("NSAutoreleasePool class not found"); let pool: *mut c_void = msg_send![cls.as_ptr(), alloc]; let pool: *mut c_void = msg_send![pool, init]; let pool = unsafe { Id::from_raw(pool as *mut _) }.expect("NSAutoreleasePool init failed"); AutoreleasePool { pool } }}
impl Drop for AutoreleasePool { fn drop(&mut self) { let _: *mut c_void = msg_send![self.pool.as_ptr(), drain]; }}
// ---------------------------------------------------------------------------// NSApplication wrapper// ---------------------------------------------------------------------------
/// Wrapper around `NSApplication`.pub struct App { app: Id,}
impl App { /// Get the shared `NSApplication` instance. /// /// Must be called from the main thread. Creates the application object /// if it doesn't already exist. pub fn shared() -> App { let cls = class!("NSApplication").expect("NSApplication class not found"); let app: *mut c_void = msg_send![cls.as_ptr(), sharedApplication]; let app = unsafe { Id::from_raw(app as *mut _) }.expect("sharedApplication returned nil"); App { app } }
/// Set the application's activation policy. /// /// Use [`NS_APPLICATION_ACTIVATION_POLICY_REGULAR`] for a normal app that /// appears in the Dock. pub fn set_activation_policy(&self, policy: i64) { let _: bool = msg_send![self.app.as_ptr(), setActivationPolicy: policy]; }
/// Activate the application, bringing it to the foreground. pub fn activate(&self) { let _: *mut c_void = msg_send![self.app.as_ptr(), activateIgnoringOtherApps: true]; }
/// Start the application's main event loop. /// /// This method does **not** return under normal circumstances. pub fn run(&self) { let _: *mut c_void = msg_send![self.app.as_ptr(), run]; }
/// Return the underlying Objective-C object. pub fn id(&self) -> Id { self.app }}
// ---------------------------------------------------------------------------// NSWindow wrapper// ---------------------------------------------------------------------------
/// Wrapper around `NSWindow`.pub struct Window { window: Id,}
impl Window { /// Create a new window with the given content rect, style mask, and backing. /// /// # Arguments /// /// * `rect` — The content rectangle (position and size). /// * `style` — Bitwise OR of `NS_WINDOW_STYLE_MASK_*` constants. /// * `backing` — Backing store type (use [`NS_BACKING_STORE_BUFFERED`]). /// * `defer` — Whether to defer window device creation. pub fn new(rect: NSRect, style: u64, backing: u64, defer: bool) -> Window { let cls = class!("NSWindow").expect("NSWindow class not found"); let window: *mut c_void = msg_send![cls.as_ptr(), alloc]; let window: *mut c_void = msg_send![ window, initWithContentRect: rect, styleMask: style, backing: backing, defer: defer ]; let window = unsafe { Id::from_raw(window as *mut _) }.expect("NSWindow initWithContentRect failed"); Window { window } }
/// Set the window's title. pub fn set_title(&self, title: &str) { let cf_title = CfString::new(title).expect("failed to create CFString for title"); // CFStringRef is toll-free bridged to NSString*. let _: *mut c_void = msg_send![self.window.as_ptr(), setTitle: cf_title.as_void_ptr()]; }
/// Make the window the key window and bring it to the front. pub fn make_key_and_order_front(&self) { let _: *mut c_void = msg_send![self.window.as_ptr(), makeKeyAndOrderFront: std::ptr::null::<c_void>()]; }
/// Get the window's content view. pub fn content_view(&self) -> Id { let view: *mut c_void = msg_send![self.window.as_ptr(), contentView]; unsafe { Id::from_raw(view as *mut _) }.expect("contentView returned nil") }
/// Set the window's content view. pub fn set_content_view(&self, view: &Id) { let _: *mut c_void = msg_send![self.window.as_ptr(), setContentView: view.as_ptr()]; }
/// Set the window's delegate. pub fn set_delegate(&self, delegate: &Id) { let _: *mut c_void = msg_send![self.window.as_ptr(), setDelegate: delegate.as_ptr()]; }
/// Enable or disable mouse-moved event delivery for this window. /// /// Must be set to `true` for `mouseMoved:` events to reach the view. pub fn set_accepts_mouse_moved_events(&self, accepts: bool) { let _: *mut c_void = msg_send![self.window.as_ptr(), setAcceptsMouseMovedEvents: accepts]; }
/// Return the underlying Objective-C object. pub fn id(&self) -> Id { self.window }}
// ---------------------------------------------------------------------------// BitmapView — custom NSView subclass for rendering a CG bitmap// ---------------------------------------------------------------------------
/// The name of the ivar storing the BitmapContext pointer in WeView.const BITMAP_CTX_IVAR: &CStr = c"_bitmapCtx";
/// Register the `WeView` custom NSView subclass.////// The class overrides `drawRect:` to blit a CGImage from the associated/// BitmapContext into the view's graphics context, and `isFlipped` to use a/// top-left coordinate origin.fn register_we_view_class() { if class!("WeView").is_some() { return; }
let superclass = class!("NSView").expect("NSView not found"); let view_class = Class::allocate(superclass, c"WeView", 0).expect("failed to allocate WeView class");
// Add an ivar to hold a raw pointer to a BitmapContext. // Size = 8 (pointer), alignment = 3 (log2(8)), type = "^v" (pointer to void). view_class.add_ivar(BITMAP_CTX_IVAR, 8, 3, c"^v");
// drawRect: extern "C" fn draw_rect(this: *mut c_void, _sel: *mut c_void, _dirty_rect: NSRect) { let this_id = unsafe { Id::from_raw(this as *mut _) }; let this_id = match this_id { Some(id) => id, None => return, };
// Get the BitmapContext pointer from our ivar. let ctx_ptr = unsafe { this_id.get_ivar(BITMAP_CTX_IVAR) }; if ctx_ptr.is_null() { return; } let bitmap_ctx = unsafe { &*(ctx_ptr as *const BitmapContext) };
// Create a CGImage from the bitmap context. let image = match bitmap_ctx.create_image() { Some(img) => img, None => return, };
// Get the view's bounds: [self bounds] let bounds: NSRect = msg_send![this, bounds];
// Get the current NSGraphicsContext: // [NSGraphicsContext currentContext] let gfx_ctx_cls = class!("NSGraphicsContext").expect("NSGraphicsContext not found"); let gfx_ctx: *mut c_void = msg_send![gfx_ctx_cls.as_ptr(), currentContext]; if gfx_ctx.is_null() { return; }
// Get the CGContextRef: [gfxCtx CGContext] let cg_context: *mut c_void = msg_send![gfx_ctx, CGContext]; if cg_context.is_null() { return; }
// Draw the image into the CG context. let rect = CGRect::new( bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height, ); unsafe { cg::draw_image_in_context(cg_context, rect, &image); } }
let sel = Sel::register(c"drawRect:"); view_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(draw_rect as *const ()) }, c"v@:{CGRect={CGPoint=dd}{CGSize=dd}}", );
// isFlipped -> YES (top-left origin) extern "C" fn is_flipped(_this: *mut c_void, _sel: *mut c_void) -> bool { true }
let sel = Sel::register(c"isFlipped"); view_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(is_flipped as *const ()) }, c"B@:", );
// acceptsFirstResponder -> YES (allows view to receive key events) extern "C" fn accepts_first_responder(_this: *mut c_void, _sel: *mut c_void) -> bool { true }
let sel = Sel::register(c"acceptsFirstResponder"); view_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(accepts_first_responder as *const ()) }, c"B@:", );
// keyDown: — log key character and keyCode to stdout extern "C" fn key_down(_this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { let chars: *mut c_void = msg_send![event, characters]; if chars.is_null() { return; } let utf8: *const c_char = msg_send![chars, UTF8String]; if utf8.is_null() { return; } let c_str = unsafe { CStr::from_ptr(utf8) }; let key_code: u16 = msg_send![event, keyCode]; if let Ok(s) = c_str.to_str() { println!("keyDown: '{}' (keyCode: {})", s, key_code); } }
let sel = Sel::register(c"keyDown:"); view_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(key_down as *const ()) }, c"v@:@", );
// mouseDown: — log mouse location to stdout extern "C" fn mouse_down(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { let raw_loc: NSPoint = msg_send![event, locationInWindow]; let loc: NSPoint = msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; println!("mouseDown: ({:.1}, {:.1})", loc.x, loc.y); }
let sel = Sel::register(c"mouseDown:"); view_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(mouse_down as *const ()) }, c"v@:@", );
// mouseUp: — log mouse location to stdout extern "C" fn mouse_up(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { let raw_loc: NSPoint = msg_send![event, locationInWindow]; let loc: NSPoint = msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; println!("mouseUp: ({:.1}, {:.1})", loc.x, loc.y); }
let sel = Sel::register(c"mouseUp:"); view_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(mouse_up as *const ()) }, c"v@:@", );
// mouseMoved: — log mouse location to stdout extern "C" fn mouse_moved(this: *mut c_void, _sel: *mut c_void, event: *mut c_void) { let raw_loc: NSPoint = msg_send![event, locationInWindow]; let loc: NSPoint = msg_send![this, convertPoint: raw_loc, fromView: std::ptr::null_mut::<c_void>()]; println!("mouseMoved: ({:.1}, {:.1})", loc.x, loc.y); }
let sel = Sel::register(c"mouseMoved:"); view_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(mouse_moved as *const ()) }, c"v@:@", );
view_class.register();}
/// A custom NSView backed by a [`BitmapContext`].////// When the view needs to draw, it creates a `CGImage` from the bitmap/// context and blits it into the view's graphics context.////// The `BitmapContext` must outlive this view. The caller is responsible/// for ensuring this (typically by keeping the context in a `Box` alongside/// the view).pub struct BitmapView { view: Id,}
impl BitmapView { /// Create a new `BitmapView` with the given frame and bitmap context. /// /// The `bitmap_ctx` pointer is stored in the view's instance variable. /// The caller must ensure the `BitmapContext` outlives this view. pub fn new(frame: NSRect, bitmap_ctx: &BitmapContext) -> BitmapView { register_we_view_class();
let cls = class!("WeView").expect("WeView class not found"); let view: *mut c_void = msg_send![cls.as_ptr(), alloc]; let view: *mut c_void = msg_send![view, initWithFrame: frame]; let view = unsafe { Id::from_raw(view as *mut _) }.expect("WeView initWithFrame failed");
// Store the BitmapContext pointer in the ivar. unsafe { view.set_ivar( BITMAP_CTX_IVAR, bitmap_ctx as *const BitmapContext as *mut c_void, ); }
BitmapView { view } }
/// Update the bitmap context pointer stored in the view. /// /// Call this when the bitmap context has been replaced (e.g., on resize). /// The new `BitmapContext` must outlive this view. pub fn update_bitmap(&self, bitmap_ctx: &BitmapContext) { unsafe { self.view.set_ivar( BITMAP_CTX_IVAR, bitmap_ctx as *const BitmapContext as *mut c_void, ); } }
/// Request the view to redraw. /// /// Call this after modifying the bitmap context's pixels to /// schedule a redraw. pub fn set_needs_display(&self) { let _: *mut c_void = msg_send![self.view.as_ptr(), setNeedsDisplay: true]; }
/// Return the underlying Objective-C view object. pub fn id(&self) -> Id { self.view }}
// ---------------------------------------------------------------------------// Global resize handler// ---------------------------------------------------------------------------
/// Global resize callback, called from `windowDidResize:` with the new/// content view dimensions (width, height) in points.////// # Safety////// Accessed only from the main thread (the AppKit event loop).static mut RESIZE_HANDLER: Option<fn(f64, f64)> = None;
/// Register a function to be called when the window is resized.////// The handler receives the new content view width and height in points./// Only one handler can be active at a time; setting a new one replaces/// any previous handler.pub fn set_resize_handler(handler: fn(f64, f64)) { // SAFETY: Called from the main thread before `app.run()`. unsafe { RESIZE_HANDLER = Some(handler); }}
// ---------------------------------------------------------------------------// Window delegate for handling resize and close events// ---------------------------------------------------------------------------
/// Register the `WeWindowDelegate` class if not already registered.////// The class implements:/// - `windowDidResize:` — marks the content view as needing display/// - `windowShouldClose:` — returns YES (allows closing)fn register_we_window_delegate_class() { if class!("WeWindowDelegate").is_some() { return; }
let superclass = class!("NSObject").expect("NSObject not found"); let delegate_class = Class::allocate(superclass, c"WeWindowDelegate", 0) .expect("failed to allocate WeWindowDelegate class");
// windowDidResize: — call resize handler and mark view as needing display extern "C" fn window_did_resize( _this: *mut c_void, _sel: *mut c_void, notification: *mut c_void, ) { let win: *mut c_void = msg_send![notification, object]; if win.is_null() { return; } let content_view: *mut c_void = msg_send![win, contentView]; if content_view.is_null() { return; } // Get the content view's bounds to determine new dimensions. let bounds: NSRect = msg_send![content_view, bounds]; // Call the resize handler if one has been registered. // SAFETY: We are on the main thread (AppKit event loop). unsafe { if let Some(handler) = RESIZE_HANDLER { handler(bounds.size.width, bounds.size.height); } } let _: *mut c_void = msg_send![content_view, setNeedsDisplay: true]; }
let sel = Sel::register(c"windowDidResize:"); delegate_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(window_did_resize as *const ()) }, c"v@:@", );
// windowShouldClose: -> YES extern "C" fn window_should_close( _this: *mut c_void, _sel: *mut c_void, _sender: *mut c_void, ) -> bool { true }
let sel = Sel::register(c"windowShouldClose:"); delegate_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>(window_should_close as *const ()) }, c"B@:@", );
delegate_class.register();}
/// Install a window delegate that handles resize and close events.////// Creates a `WeWindowDelegate` class (if not already registered) and sets/// an instance as the window's delegate. The app delegate then terminates/// the app when the last window closes.pub fn install_window_delegate(window: &Window) { register_we_window_delegate_class();
let cls = class!("WeWindowDelegate").expect("WeWindowDelegate not found"); let delegate: *mut c_void = msg_send![cls.as_ptr(), alloc]; let delegate: *mut c_void = msg_send![delegate, init]; let delegate = unsafe { Id::from_raw(delegate as *mut _) }.expect("WeWindowDelegate init failed"); window.set_delegate(&delegate);}
// ---------------------------------------------------------------------------// App delegate for handling window close -> app termination// ---------------------------------------------------------------------------
/// Install an application delegate that terminates the app when the last/// window is closed.////// This creates a custom Objective-C class `WeAppDelegate` that implements/// `applicationShouldTerminateAfterLastWindowClosed:` returning `YES`.pub fn install_app_delegate(app: &App) { // Only register the delegate class once. if class!("WeAppDelegate").is_some() { // Already registered, just create an instance and set it. set_delegate(app); return; }
let superclass = class!("NSObject").expect("NSObject not found"); let delegate_class = Class::allocate(superclass, c"WeAppDelegate", 0) .expect("failed to allocate WeAppDelegate class");
// applicationShouldTerminateAfterLastWindowClosed: extern "C" fn should_terminate_after_last_window_closed( _this: *mut c_void, _sel: *mut c_void, _app: *mut c_void, ) -> bool { true }
let sel = Sel::register(c"applicationShouldTerminateAfterLastWindowClosed:"); delegate_class.add_method( sel, unsafe { std::mem::transmute::<*const (), Imp>( should_terminate_after_last_window_closed as *const (), ) }, c"B@:@", );
delegate_class.register(); set_delegate(app);}
fn set_delegate(app: &App) { let cls = class!("WeAppDelegate").expect("WeAppDelegate not found"); let delegate: *mut c_void = msg_send![cls.as_ptr(), alloc]; let delegate: *mut c_void = msg_send![delegate, init]; let _: *mut c_void = msg_send![app.id().as_ptr(), setDelegate: delegate];}
// ---------------------------------------------------------------------------// Convenience: create a standard browser window// ---------------------------------------------------------------------------
/// Create a standard window suitable for a browser.////// Returns a window with title bar, close, minimize, and resize controls,/// centered at (200, 200), sized 800x600.pub fn create_standard_window(title: &str) -> Window { let style = NS_WINDOW_STYLE_MASK_TITLED | NS_WINDOW_STYLE_MASK_CLOSABLE | NS_WINDOW_STYLE_MASK_MINIATURIZABLE | NS_WINDOW_STYLE_MASK_RESIZABLE;
let rect = NSRect::new(200.0, 200.0, 800.0, 600.0); let window = Window::new(rect, style, NS_BACKING_STORE_BUFFERED, false); window.set_title(title); window}
// ---------------------------------------------------------------------------// Tests// ---------------------------------------------------------------------------
#[cfg(test)]mod tests { use super::*;
#[test] fn nsrect_new() { let rect = NSRect::new(10.0, 20.0, 300.0, 400.0); assert_eq!(rect.origin.x, 10.0); assert_eq!(rect.origin.y, 20.0); assert_eq!(rect.size.width, 300.0); assert_eq!(rect.size.height, 400.0); }
#[test] fn style_mask_constants() { // Verify the constants match AppKit's expected values. assert_eq!(NS_WINDOW_STYLE_MASK_TITLED, 1); assert_eq!(NS_WINDOW_STYLE_MASK_CLOSABLE, 2); assert_eq!(NS_WINDOW_STYLE_MASK_MINIATURIZABLE, 4); assert_eq!(NS_WINDOW_STYLE_MASK_RESIZABLE, 8); }
#[test] fn backing_store_constant() { assert_eq!(NS_BACKING_STORE_BUFFERED, 2); }
#[test] fn activation_policy_constant() { assert_eq!(NS_APPLICATION_ACTIVATION_POLICY_REGULAR, 0); }
#[test] fn autorelease_pool_create_and_drop() { // Creating and dropping an autorelease pool should not crash. let _pool = AutoreleasePool::new(); }
#[test] fn combined_style_mask() { let style = NS_WINDOW_STYLE_MASK_TITLED | NS_WINDOW_STYLE_MASK_CLOSABLE | NS_WINDOW_STYLE_MASK_MINIATURIZABLE | NS_WINDOW_STYLE_MASK_RESIZABLE; assert_eq!(style, 0b1111); }
#[test] fn we_view_class_registration() { register_we_view_class(); let cls = class!("WeView"); assert!(cls.is_some(), "WeView class should be registered"); }
#[test] fn bitmap_view_create() { let _pool = AutoreleasePool::new(); let bitmap = BitmapContext::new(100, 100).expect("should create bitmap context"); let frame = NSRect::new(0.0, 0.0, 100.0, 100.0); let view = BitmapView::new(frame, &bitmap); assert!(!view.id().as_ptr().is_null()); }
#[test] fn we_view_accepts_first_responder() { let _pool = AutoreleasePool::new(); let bitmap = BitmapContext::new(100, 100).expect("should create bitmap context"); let frame = NSRect::new(0.0, 0.0, 100.0, 100.0); let view = BitmapView::new(frame, &bitmap); let accepts: bool = msg_send![view.id().as_ptr(), acceptsFirstResponder]; assert!(accepts, "WeView should accept first responder"); }
#[test] fn we_view_responds_to_key_down() { let _pool = AutoreleasePool::new(); register_we_view_class(); let cls = class!("WeView").expect("WeView should be registered"); let sel = Sel::register(c"keyDown:"); let instances_respond: bool = msg_send![cls.as_ptr(), instancesRespondToSelector: sel.as_ptr()]; assert!(instances_respond, "WeView should respond to keyDown:"); }
#[test] fn we_view_responds_to_mouse_events() { let _pool = AutoreleasePool::new(); register_we_view_class(); let cls = class!("WeView").expect("WeView should be registered");
for sel_name in [c"mouseDown:", c"mouseUp:", c"mouseMoved:"] { let sel = Sel::register(sel_name); let responds: bool = msg_send![cls.as_ptr(), instancesRespondToSelector: sel.as_ptr()]; assert!(responds, "WeView should respond to {:?}", sel_name); } }
#[test] fn we_window_delegate_class_registration() { register_we_window_delegate_class(); let cls = class!("WeWindowDelegate"); assert!(cls.is_some(), "WeWindowDelegate class should be registered"); }
#[test] fn we_window_delegate_responds_to_resize() { register_we_window_delegate_class(); let cls = class!("WeWindowDelegate").expect("WeWindowDelegate should be registered"); let sel = Sel::register(c"windowDidResize:"); let responds: bool = msg_send![cls.as_ptr(), instancesRespondToSelector: sel.as_ptr()]; assert!( responds, "WeWindowDelegate should respond to windowDidResize:" ); }}