Some more updates for macOS.
- Added support for basic Cursor management. - Added support for NSWindow cancelOperation: callbacks. It's not... perfect, but it works as a discrete hook. - Added support for NSProgressIndicator. - Properly forward Error types from QuickLook generation calls, and future-proof the ThumbnailQuality enum. - Add support for configuring Label line break mode.
This commit is contained in:
parent
121a2f938e
commit
62cebab691
|
@ -24,6 +24,10 @@ impl From<NSInteger> for ModalResponse {
|
||||||
-1000 => ModalResponse::Stopped,
|
-1000 => ModalResponse::Stopped,
|
||||||
-1001 => ModalResponse::Aborted,
|
-1001 => ModalResponse::Aborted,
|
||||||
-1002 => ModalResponse::Continue,
|
-1002 => ModalResponse::Continue,
|
||||||
|
|
||||||
|
// @TODO: Definitely don't panic here, wtf was I thinking?
|
||||||
|
// Probably make this a ModalResponse::Unknown or something so a user can
|
||||||
|
// gracefully handle.
|
||||||
e => { panic!("Unknown NSModalResponse sent back! {}", e); }
|
e => { panic!("Unknown NSModalResponse sent back! {}", e); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,6 +106,7 @@ pub mod listview;
|
||||||
pub mod networking;
|
pub mod networking;
|
||||||
pub mod notification_center;
|
pub mod notification_center;
|
||||||
pub mod pasteboard;
|
pub mod pasteboard;
|
||||||
|
pub mod progress;
|
||||||
pub mod scrollview;
|
pub mod scrollview;
|
||||||
pub mod text;
|
pub mod text;
|
||||||
|
|
||||||
|
|
85
src/macos/cursor.rs
Normal file
85
src/macos/cursor.rs
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
|
||||||
|
use objc::{class, msg_send, sel, sel_impl};
|
||||||
|
use crate::foundation::id;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CursorType {
|
||||||
|
Arrow,
|
||||||
|
Crosshair,
|
||||||
|
ClosedHand,
|
||||||
|
OpenHand,
|
||||||
|
PointingHand,
|
||||||
|
|
||||||
|
ResizeLeft,
|
||||||
|
ResizeRight,
|
||||||
|
ResizeLeftRight,
|
||||||
|
|
||||||
|
ResizeUp,
|
||||||
|
ResizeDown,
|
||||||
|
ResizeUpDown,
|
||||||
|
|
||||||
|
DisappearingItem,
|
||||||
|
|
||||||
|
IBeam,
|
||||||
|
IBeamVertical,
|
||||||
|
|
||||||
|
OperationNotAllowed,
|
||||||
|
DragLink,
|
||||||
|
DragCopy,
|
||||||
|
ContextMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Cursor;
|
||||||
|
|
||||||
|
impl Cursor {
|
||||||
|
/// Given a cursor type, will make it the system cursor.
|
||||||
|
/// The inverse of this call, which you should call when ready, is `pop()`.
|
||||||
|
pub fn push(cursor_type: CursorType) {
|
||||||
|
unsafe {
|
||||||
|
let cursor: id = match cursor_type {
|
||||||
|
CursorType::Arrow => msg_send![class!(NSCursor), arrowCursor],
|
||||||
|
CursorType::Crosshair => msg_send![class!(NSCursor), crosshairCursor],
|
||||||
|
CursorType::ClosedHand => msg_send![class!(NSCursor), closedHandCursor],
|
||||||
|
CursorType::OpenHand => msg_send![class!(NSCursor), openHandCursor],
|
||||||
|
CursorType::PointingHand => msg_send![class!(NSCursor), pointingHandCursor],
|
||||||
|
CursorType::ResizeLeft => msg_send![class!(NSCursor), resizeLeftCursor],
|
||||||
|
CursorType::ResizeRight => msg_send![class!(NSCursor), resizeRightCursor],
|
||||||
|
CursorType::ResizeLeftRight => msg_send![class!(NSCursor), resizeLeftRightCursor],
|
||||||
|
CursorType::ResizeUp => msg_send![class!(NSCursor), resizeUpCursor],
|
||||||
|
CursorType::ResizeDown => msg_send![class!(NSCursor), resizeDownCursor],
|
||||||
|
CursorType::ResizeUpDown => msg_send![class!(NSCursor), resizeUpDownCursor],
|
||||||
|
CursorType::DisappearingItem => msg_send![class!(NSCursor), disappearingItemCursor],
|
||||||
|
CursorType::IBeam => msg_send![class!(NSCursor), IBeamCursor],
|
||||||
|
CursorType::IBeamVertical => msg_send![class!(NSCursor), IBeamCursorForVerticalLayout],
|
||||||
|
CursorType::OperationNotAllowed => msg_send![class!(NSCursor), operationNotAllowedCursor],
|
||||||
|
CursorType::DragLink => msg_send![class!(NSCursor), dragLinkCursor],
|
||||||
|
CursorType::DragCopy => msg_send![class!(NSCursor), dragCopyCursor],
|
||||||
|
CursorType::ContextMenu => msg_send![class!(NSCursor), contextualMenuCursor]
|
||||||
|
};
|
||||||
|
|
||||||
|
let _: () = msg_send![cursor, push];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pops the current cursor off the cursor-stack. The inverse of push.
|
||||||
|
pub fn pop() {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![class!(NSCursor), pop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Hides the cursor. Part of a balanced call stack.
|
||||||
|
pub fn hide() {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![class!(NSCursor), hide];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Un-hides the cursor. Part of a balanced call stack.
|
||||||
|
pub fn unhide() {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![class!(NSCursor), unhide];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,9 @@ pub use alert::Alert;
|
||||||
mod app;
|
mod app;
|
||||||
pub use app::*;
|
pub use app::*;
|
||||||
|
|
||||||
|
mod cursor;
|
||||||
|
pub use cursor::{Cursor, CursorType};
|
||||||
|
|
||||||
pub mod menu;
|
pub mod menu;
|
||||||
pub mod printing;
|
pub mod printing;
|
||||||
pub mod toolbar;
|
pub mod toolbar;
|
||||||
|
|
|
@ -219,6 +219,15 @@ extern fn did_expose<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||||
window.did_expose();
|
window.did_expose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called as part of the responder chain, when, say, the ESC key is hit. If your
|
||||||
|
/// delegate returns `true` in `should_cancel_on_esc`, then this will allow your
|
||||||
|
/// window to close when the Esc key is hit. This is mostly useful for Sheet-presented
|
||||||
|
/// windows, and so the default response from delegates is `false` and must be opted in to.
|
||||||
|
extern fn cancel<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
||||||
|
let window = load::<T>(this, WINDOW_DELEGATE_PTR);
|
||||||
|
window.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
/// Injects an `NSWindow` subclass, with some callback and pointer ivars for what we
|
/// Injects an `NSWindow` subclass, with some callback and pointer ivars for what we
|
||||||
/// need to do.
|
/// need to do.
|
||||||
pub(crate) fn register_window_class() -> *const Class {
|
pub(crate) fn register_window_class() -> *const Class {
|
||||||
|
@ -292,6 +301,7 @@ pub(crate) fn register_window_class_with_delegate<T: WindowDelegate>() -> *const
|
||||||
decl.add_method(sel!(windowDidChangeOcclusionState:), did_change_occlusion_state::<T> as extern fn(&Object, _, _));
|
decl.add_method(sel!(windowDidChangeOcclusionState:), did_change_occlusion_state::<T> as extern fn(&Object, _, _));
|
||||||
decl.add_method(sel!(windowDidExpose:), did_expose::<T> as extern fn(&Object, _, _));
|
decl.add_method(sel!(windowDidExpose:), did_expose::<T> as extern fn(&Object, _, _));
|
||||||
decl.add_method(sel!(windowDidUpdate:), did_update::<T> as extern fn(&Object, _, _));
|
decl.add_method(sel!(windowDidUpdate:), did_update::<T> as extern fn(&Object, _, _));
|
||||||
|
decl.add_method(sel!(cancelOperation:), cancel::<T> as extern fn (&Object, _, _));
|
||||||
|
|
||||||
DELEGATE_CLASS = decl.register();
|
DELEGATE_CLASS = decl.register();
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,7 +52,7 @@ impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
||||||
/// Allocates and configures an `NSWindowController` in the Objective-C/Cocoa runtime that maps over
|
/// Allocates and configures an `NSWindowController` in the Objective-C/Cocoa runtime that maps over
|
||||||
/// to your supplied delegate.
|
/// to your supplied delegate.
|
||||||
pub fn with(config: WindowConfig, delegate: T) -> Self {
|
pub fn with(config: WindowConfig, delegate: T) -> Self {
|
||||||
let window = Window::with(config, delegate);
|
let mut window = Window::with(config, delegate);
|
||||||
|
|
||||||
let objc = unsafe {
|
let objc = unsafe {
|
||||||
let window_controller_class = register_window_controller_class::<T>();
|
let window_controller_class = register_window_controller_class::<T>();
|
||||||
|
@ -67,7 +67,7 @@ impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
||||||
ShareId::from_ptr(controller)
|
ShareId::from_ptr(controller)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(delegate) = &window.delegate {
|
if let Some(delegate) = &mut window.delegate {
|
||||||
(*delegate).did_load(Window {
|
(*delegate).did_load(Window {
|
||||||
delegate: None,
|
delegate: None,
|
||||||
objc: window.objc.clone()
|
objc: window.objc.clone()
|
||||||
|
|
|
@ -10,10 +10,12 @@
|
||||||
|
|
||||||
use std::unreachable;
|
use std::unreachable;
|
||||||
|
|
||||||
|
use block::ConcreteBlock;
|
||||||
|
|
||||||
use core_graphics::base::CGFloat;
|
use core_graphics::base::CGFloat;
|
||||||
use core_graphics::geometry::{CGRect, CGSize};
|
use core_graphics::geometry::{CGRect, CGSize};
|
||||||
|
|
||||||
use objc::{msg_send, sel, sel_impl};
|
use objc::{msg_send, sel, sel_impl, class};
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
|
@ -66,6 +68,11 @@ impl Window {
|
||||||
/// after we initialize the backing `NSWindow`.
|
/// after we initialize the backing `NSWindow`.
|
||||||
pub fn new(config: WindowConfig) -> Window {
|
pub fn new(config: WindowConfig) -> Window {
|
||||||
let objc = unsafe {
|
let objc = unsafe {
|
||||||
|
// This behavior might make sense to keep as default (YES), but I think the majority of
|
||||||
|
// apps that would use this toolkit wouldn't be tab-oriented...
|
||||||
|
let _: () = msg_send![class!(NSWindow), setAllowsAutomaticWindowTabbing:NO];
|
||||||
|
|
||||||
|
|
||||||
let alloc: id = msg_send![register_window_class(), alloc];
|
let alloc: id = msg_send![register_window_class(), alloc];
|
||||||
|
|
||||||
// Other types of backing (Retained/NonRetained) are archaic, dating back to the
|
// Other types of backing (Retained/NonRetained) are archaic, dating back to the
|
||||||
|
@ -105,9 +112,14 @@ impl<T> Window<T> where T: WindowDelegate + 'static {
|
||||||
/// enables easier structure of your codebase, and in a way simulates traditional class based
|
/// enables easier structure of your codebase, and in a way simulates traditional class based
|
||||||
/// architectures... just without the subclassing.
|
/// architectures... just without the subclassing.
|
||||||
pub fn with(config: WindowConfig, delegate: T) -> Self {
|
pub fn with(config: WindowConfig, delegate: T) -> Self {
|
||||||
let delegate = Box::new(delegate);
|
let mut delegate = Box::new(delegate);
|
||||||
|
|
||||||
let objc = unsafe {
|
let objc = unsafe {
|
||||||
|
// This behavior might make sense to keep as default (YES), but I think the majority of
|
||||||
|
// apps that would use this toolkit wouldn't be tab-oriented...
|
||||||
|
let _: () = msg_send![class!(NSWindow), setAllowsAutomaticWindowTabbing:NO];
|
||||||
|
|
||||||
|
|
||||||
let alloc: id = msg_send![register_window_class_with_delegate::<T>(), alloc];
|
let alloc: id = msg_send![register_window_class_with_delegate::<T>(), alloc];
|
||||||
|
|
||||||
// Other types of backing (Retained/NonRetained) are archaic, dating back to the
|
// Other types of backing (Retained/NonRetained) are archaic, dating back to the
|
||||||
|
@ -143,7 +155,7 @@ impl<T> Window<T> where T: WindowDelegate + 'static {
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
&delegate.did_load(Window {
|
(&mut delegate).did_load(Window {
|
||||||
delegate: None,
|
delegate: None,
|
||||||
objc: objc.clone()
|
objc: objc.clone()
|
||||||
});
|
});
|
||||||
|
@ -447,6 +459,37 @@ impl<T> Window<T> {
|
||||||
scale as f64
|
scale as f64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Given a window and callback handler, will run it as a "sheet" (model-ish) and then run the
|
||||||
|
/// handler once the sheet is dismissed.
|
||||||
|
///
|
||||||
|
/// This is a bit awkward due to Rust semantics; you have to use the same type of Window as the
|
||||||
|
/// one you're presenting on, but in practice this isn't too bad since you rarely want a Window
|
||||||
|
/// without a WindowDelegate.
|
||||||
|
pub fn begin_sheet<F, W>(&self, window: &Window<W>, completion: F)
|
||||||
|
where
|
||||||
|
F: Fn() + Send + Sync + 'static,
|
||||||
|
W: WindowDelegate + 'static
|
||||||
|
{
|
||||||
|
let block = ConcreteBlock::new(move |response: NSInteger| {
|
||||||
|
completion();
|
||||||
|
});
|
||||||
|
let block = block.copy();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*self.objc, beginSheet:&*window.objc completionHandler:block];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Closes a sheet.
|
||||||
|
pub fn end_sheet<W>(&self, window: &Window<W>)
|
||||||
|
where
|
||||||
|
W: WindowDelegate + 'static
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*self.objc, endSheet:&*window.objc];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for Window<T> {
|
impl<T> Drop for Window<T> {
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub trait WindowDelegate {
|
||||||
/// to set up your views and what not.
|
/// to set up your views and what not.
|
||||||
///
|
///
|
||||||
/// If you're coming from the web, you can think of this as `DOMContentLoaded`.
|
/// If you're coming from the web, you can think of this as `DOMContentLoaded`.
|
||||||
fn did_load(&self, _window: Window) {}
|
fn did_load(&mut self, _window: Window) {}
|
||||||
|
|
||||||
/// Called when the user has attempted to close the window. NOT called when a user quits the
|
/// Called when the user has attempted to close the window. NOT called when a user quits the
|
||||||
/// application. Return false here if you need to handle the edge case.
|
/// application. Return false here if you need to handle the edge case.
|
||||||
|
@ -118,4 +118,8 @@ pub trait WindowDelegate {
|
||||||
|
|
||||||
/// Fired when the Window receives an `update` message from higher up in the chain.
|
/// Fired when the Window receives an `update` message from higher up in the chain.
|
||||||
fn did_update(&self) {}
|
fn did_update(&self) {}
|
||||||
|
|
||||||
|
/// If you want your window to close when the `ESC` key is hit, implement this.
|
||||||
|
/// This is mostly useful for windows that present as modal sheets.
|
||||||
|
fn cancel(&self) {}
|
||||||
}
|
}
|
||||||
|
|
20
src/progress/enums.rs
Normal file
20
src/progress/enums.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
use crate::foundation::{NSUInteger};
|
||||||
|
|
||||||
|
/// The type of spinner style you're after.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ProgressIndicatorStyle {
|
||||||
|
/// A loading bar.
|
||||||
|
Bar,
|
||||||
|
|
||||||
|
/// A spinning circle.
|
||||||
|
Spinner
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ProgressIndicatorStyle> for NSUInteger {
|
||||||
|
fn from(style: ProgressIndicatorStyle) -> Self {
|
||||||
|
match style {
|
||||||
|
ProgressIndicatorStyle::Bar => 0,
|
||||||
|
ProgressIndicatorStyle::Spinner => 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/progress/ios.rs
Normal file
45
src/progress/ios.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
|
use objc::declare::ClassDecl;
|
||||||
|
use objc::runtime::{Class, Object, Sel, BOOL};
|
||||||
|
use objc::{class, sel, sel_impl};
|
||||||
|
use objc_id::Id;
|
||||||
|
|
||||||
|
use crate::foundation::{id, YES, NO, NSUInteger};
|
||||||
|
use crate::dragdrop::DragInfo;
|
||||||
|
use crate::view::{VIEW_DELEGATE_PTR, ViewDelegate};
|
||||||
|
use crate::utils::load;
|
||||||
|
|
||||||
|
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
||||||
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
|
/// used if there's no delegates.
|
||||||
|
pub(crate) fn register_view_class() -> *const Class {
|
||||||
|
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||||
|
static INIT: Once = Once::new();
|
||||||
|
|
||||||
|
INIT.call_once(|| unsafe {
|
||||||
|
let superclass = class!(UIView);
|
||||||
|
let mut decl = ClassDecl::new("RSTView", superclass).unwrap();
|
||||||
|
VIEW_CLASS = decl.register();
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe { VIEW_CLASS }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Injects an `NSView` subclass, with some callback and pointer ivars for what we
|
||||||
|
/// need to do.
|
||||||
|
pub(crate) fn register_view_class_with_delegate<T: ViewDelegate>() -> *const Class {
|
||||||
|
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||||
|
static INIT: Once = Once::new();
|
||||||
|
|
||||||
|
INIT.call_once(|| unsafe {
|
||||||
|
let superclass = class!(UIView);
|
||||||
|
let mut decl = ClassDecl::new("RSTViewWithDelegate", superclass).unwrap();
|
||||||
|
decl.add_ivar::<usize>(VIEW_DELEGATE_PTR);
|
||||||
|
VIEW_CLASS = decl.register();
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
VIEW_CLASS
|
||||||
|
}
|
||||||
|
}
|
21
src/progress/macos.rs
Normal file
21
src/progress/macos.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
|
use objc::declare::ClassDecl;
|
||||||
|
use objc::runtime::{Class, Object, Sel, BOOL};
|
||||||
|
use objc::{class, sel, sel_impl};
|
||||||
|
|
||||||
|
/// Injects an `NSView` subclass. This is used for the default views that don't use delegates - we
|
||||||
|
/// have separate classes here since we don't want to waste cycles on methods that will never be
|
||||||
|
/// used if there's no delegates.
|
||||||
|
pub(crate) fn register_progress_indicator_class() -> *const Class {
|
||||||
|
static mut VIEW_CLASS: *const Class = 0 as *const Class;
|
||||||
|
static INIT: Once = Once::new();
|
||||||
|
|
||||||
|
INIT.call_once(|| unsafe {
|
||||||
|
let superclass = class!(NSProgressIndicator);
|
||||||
|
let decl = ClassDecl::new("RSTProgressIndicator", superclass).unwrap();
|
||||||
|
VIEW_CLASS = decl.register();
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe { VIEW_CLASS }
|
||||||
|
}
|
166
src/progress/mod.rs
Normal file
166
src/progress/mod.rs
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
use objc_id::ShareId;
|
||||||
|
use objc::runtime::{Class, Object};
|
||||||
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
|
use crate::foundation::{id, nil, YES, NO, NSUInteger};
|
||||||
|
use crate::color::Color;
|
||||||
|
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
mod macos;
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
use macos::{register_progress_indicator_class};
|
||||||
|
|
||||||
|
#[cfg(target_os = "ios")]
|
||||||
|
mod ios;
|
||||||
|
|
||||||
|
#[cfg(target_os = "ios")]
|
||||||
|
use ios::{register_progress_indicator_class};
|
||||||
|
|
||||||
|
mod enums;
|
||||||
|
pub use enums::ProgressIndicatorStyle;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ProgressIndicator {
|
||||||
|
/// A pointer to the Objective-C runtime view controller.
|
||||||
|
pub objc: ShareId<Object>,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime top layout constraint.
|
||||||
|
pub top: LayoutAnchorY,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime leading layout constraint.
|
||||||
|
pub leading: LayoutAnchorX,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime trailing layout constraint.
|
||||||
|
pub trailing: LayoutAnchorX,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime bottom layout constraint.
|
||||||
|
pub bottom: LayoutAnchorY,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime width layout constraint.
|
||||||
|
pub width: LayoutAnchorDimension,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime height layout constraint.
|
||||||
|
pub height: LayoutAnchorDimension,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime center X layout constraint.
|
||||||
|
pub center_x: LayoutAnchorX,
|
||||||
|
|
||||||
|
/// A pointer to the Objective-C runtime center Y layout constraint.
|
||||||
|
pub center_y: LayoutAnchorY
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProgressIndicator {
|
||||||
|
fn default() -> Self {
|
||||||
|
ProgressIndicator::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgressIndicator {
|
||||||
|
/// Returns a default `ProgressIndicator`, suitable for
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let view = unsafe {
|
||||||
|
let view: id = msg_send![register_progress_indicator_class(), new];
|
||||||
|
let _: () = msg_send![view, setTranslatesAutoresizingMaskIntoConstraints:NO];
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
let _: () = msg_send![view, setWantsLayer:YES];
|
||||||
|
|
||||||
|
view
|
||||||
|
};
|
||||||
|
|
||||||
|
ProgressIndicator {
|
||||||
|
top: LayoutAnchorY::new(unsafe { msg_send![view, topAnchor] }),
|
||||||
|
leading: LayoutAnchorX::new(unsafe { msg_send![view, leadingAnchor] }),
|
||||||
|
trailing: LayoutAnchorX::new(unsafe { msg_send![view, trailingAnchor] }),
|
||||||
|
bottom: LayoutAnchorY::new(unsafe { msg_send![view, bottomAnchor] }),
|
||||||
|
width: LayoutAnchorDimension::new(unsafe { msg_send![view, widthAnchor] }),
|
||||||
|
height: LayoutAnchorDimension::new(unsafe { msg_send![view, heightAnchor] }),
|
||||||
|
center_x: LayoutAnchorX::new(unsafe { msg_send![view, centerXAnchor] }),
|
||||||
|
center_y: LayoutAnchorY::new(unsafe { msg_send![view, centerYAnchor] }),
|
||||||
|
objc: unsafe { ShareId::from_ptr(view) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProgressIndicator {
|
||||||
|
/// Call this to set the background color for the backing layer.
|
||||||
|
pub fn set_background_color(&self, color: Color) {
|
||||||
|
let bg = color.into_platform_specific_color();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let cg: id = msg_send![bg, CGColor];
|
||||||
|
let layer: id = msg_send![&*self.objc, layer];
|
||||||
|
let _: () = msg_send![layer, setBackgroundColor:cg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts the animation for an indeterminate indicator.
|
||||||
|
pub fn start_animation(&self) {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*self.objc, startAnimation:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop_animation(&self) {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*self.objc, stopAnimation:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increment(&self, by: f64) {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*self.objc, incrementBy:by];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_style(&self, style: ProgressIndicatorStyle) {
|
||||||
|
unsafe {
|
||||||
|
let style = style as NSUInteger;
|
||||||
|
let _: () = msg_send![&*self.objc, setStyle:style];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_indeterminate(&self, is_indeterminate: bool) {
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*self.objc, setIndeterminate:match is_indeterminate {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layout for ProgressIndicator {
|
||||||
|
fn get_backing_node(&self) -> ShareId<Object> {
|
||||||
|
self.objc.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_subview<V: Layout>(&self, view: &V) {
|
||||||
|
let backing_node = view.get_backing_node();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let _: () = msg_send![&*self.objc, addSubview:backing_node];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ProgressIndicator {
|
||||||
|
/// A bit of extra cleanup for delegate callback pointers. If the originating `ProgressIndicator` is being
|
||||||
|
/// dropped, we do some logic to clean it all up (e.g, we go ahead and check to see if
|
||||||
|
/// this has a superview (i.e, it's in the heirarchy) on the AppKit side. If it does, we go
|
||||||
|
/// ahead and remove it - this is intended to match the semantics of how Rust handles things).
|
||||||
|
///
|
||||||
|
/// There are, thankfully, no delegates we need to break here.
|
||||||
|
fn drop(&mut self) {
|
||||||
|
/*if self.delegate.is_some() {
|
||||||
|
unsafe {
|
||||||
|
let superview: id = msg_send![&*self.objc, superview];
|
||||||
|
if superview != nil {
|
||||||
|
let _: () = msg_send![&*self.objc, removeFromSuperview];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,12 @@ pub enum ThumbnailQuality {
|
||||||
|
|
||||||
/// Ask for them all, and pick which one you
|
/// Ask for them all, and pick which one you
|
||||||
/// use via your provided callback.
|
/// use via your provided callback.
|
||||||
All
|
All,
|
||||||
|
|
||||||
|
/// Provided in case this is ever expanded by the OS, and the system
|
||||||
|
/// returns a thumbnail quality type that can't be matched here. Users
|
||||||
|
/// could then handle the edge case themselves.
|
||||||
|
Unknown(NSUInteger)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&ThumbnailQuality> for NSUInteger {
|
impl From<&ThumbnailQuality> for NSUInteger {
|
||||||
|
@ -32,11 +37,35 @@ impl From<&ThumbnailQuality> for NSUInteger {
|
||||||
ThumbnailQuality::Icon => 1 << 0,
|
ThumbnailQuality::Icon => 1 << 0,
|
||||||
ThumbnailQuality::Low => 1 << 1,
|
ThumbnailQuality::Low => 1 << 1,
|
||||||
ThumbnailQuality::High => 1 << 2,
|
ThumbnailQuality::High => 1 << 2,
|
||||||
ThumbnailQuality::All => NSUInteger::MAX
|
ThumbnailQuality::All => NSUInteger::MAX,
|
||||||
|
ThumbnailQuality::Unknown(x) => *x
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ThumbnailQuality> for NSUInteger {
|
||||||
|
fn from(quality: ThumbnailQuality) -> Self {
|
||||||
|
match quality {
|
||||||
|
ThumbnailQuality::Icon => 1 << 0,
|
||||||
|
ThumbnailQuality::Low => 1 << 1,
|
||||||
|
ThumbnailQuality::High => 1 << 2,
|
||||||
|
ThumbnailQuality::All => NSUInteger::MAX,
|
||||||
|
ThumbnailQuality::Unknown(x) => x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NSUInteger> for ThumbnailQuality {
|
||||||
|
fn from(i: NSUInteger) -> Self {
|
||||||
|
match i {
|
||||||
|
0 => ThumbnailQuality::Icon,
|
||||||
|
2 => ThumbnailQuality::Low,
|
||||||
|
4 => ThumbnailQuality::High,
|
||||||
|
NSUInteger::MAX => ThumbnailQuality::All,
|
||||||
|
i => ThumbnailQuality::Unknown(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ThumbnailConfig {
|
pub struct ThumbnailConfig {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use block::ConcreteBlock;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use crate::foundation::{id, NSUInteger};
|
use crate::foundation::{id, nil, NSUInteger};
|
||||||
use crate::image::Image;
|
use crate::image::Image;
|
||||||
|
|
||||||
mod config;
|
mod config;
|
||||||
|
@ -25,14 +25,19 @@ impl ThumbnailGenerator {
|
||||||
|
|
||||||
pub fn generate<F>(&self, url: &Url, config: ThumbnailConfig, callback: F)
|
pub fn generate<F>(&self, url: &Url, config: ThumbnailConfig, callback: F)
|
||||||
where
|
where
|
||||||
//F: Fn(Result<(Image, ThumbnailQuality), Error>) + Send + Sync + 'static
|
|
||||||
F: Fn(Result<(Image, ThumbnailQuality), Error>) + Send + Sync + 'static
|
F: Fn(Result<(Image, ThumbnailQuality), Error>) + Send + Sync + 'static
|
||||||
{
|
{
|
||||||
let block = ConcreteBlock::new(move |thumbnail: id, thumbnail_type: NSUInteger, error: id| {
|
let block = ConcreteBlock::new(move |thumbnail: id, thumbnail_type: NSUInteger, error: id| {
|
||||||
|
if error == nil {
|
||||||
unsafe {
|
unsafe {
|
||||||
let image = Image::with(msg_send![thumbnail, NSImage]);
|
let image = Image::with(msg_send![thumbnail, NSImage]);
|
||||||
|
let quality = ThumbnailQuality::from(thumbnail_type);
|
||||||
callback(Ok((image, ThumbnailQuality::Low)));
|
callback(Ok((image, ThumbnailQuality::Low)));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let error = Error::new(error);
|
||||||
|
callback(Err(error));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let block = block.copy();
|
let block = block.copy();
|
||||||
|
|
|
@ -20,3 +20,39 @@ impl From<TextAlign> for NSInteger {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Instructs text controls how to optimize line breaks.
|
||||||
|
pub enum LineBreakMode {
|
||||||
|
/// Wrap at word boundaries (the default)
|
||||||
|
WrapWords,
|
||||||
|
|
||||||
|
/// Wrap at character boundaries
|
||||||
|
WrapChars,
|
||||||
|
|
||||||
|
/// Clip with no regard
|
||||||
|
Clip,
|
||||||
|
|
||||||
|
/// Truncate the start, e.g, ...my sentence
|
||||||
|
TruncateHead,
|
||||||
|
|
||||||
|
/// Truncate the end, e.g, my sentenc...
|
||||||
|
TruncateTail,
|
||||||
|
|
||||||
|
/// Truncate the middle, e.g, my se...ce
|
||||||
|
TruncateMiddle
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<NSUInteger> for LineBreakMode {
|
||||||
|
fn into(self) -> NSUInteger {
|
||||||
|
match self {
|
||||||
|
LineBreakMode::WrapWords => 0,
|
||||||
|
LineBreakMode::WrapChars => 1,
|
||||||
|
LineBreakMode::Clip => 2,
|
||||||
|
LineBreakMode::TruncateHead => 3,
|
||||||
|
LineBreakMode::TruncateTail => 4,
|
||||||
|
LineBreakMode::TruncateMiddle => 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,10 +44,10 @@ use objc_id::ShareId;
|
||||||
use objc::runtime::{Class, Object};
|
use objc::runtime::{Class, Object};
|
||||||
use objc::{msg_send, sel, sel_impl};
|
use objc::{msg_send, sel, sel_impl};
|
||||||
|
|
||||||
use crate::foundation::{id, nil, YES, NO, NSArray, NSInteger, NSString};
|
use crate::foundation::{id, nil, YES, NO, NSArray, NSInteger, NSUInteger, NSString};
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||||
use crate::text::{Font, TextAlign};
|
use crate::text::{Font, TextAlign, LineBreakMode};
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod macos;
|
mod macos;
|
||||||
|
@ -237,6 +237,15 @@ impl<T> Label<T> {
|
||||||
let _: () = msg_send![&*self.objc, setFont:&*font.objc];
|
let _: () = msg_send![&*self.objc, setFont:&*font.objc];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_line_break_mode(&self, mode: LineBreakMode) {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
unsafe {
|
||||||
|
let cell: id = msg_send![&*self.objc, cell];
|
||||||
|
let mode = mode as NSUInteger;
|
||||||
|
let _: () = msg_send![cell, setLineBreakMode:mode];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Layout for Label<T> {
|
impl<T> Layout for Label<T> {
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub mod label;
|
||||||
pub use label::Label;
|
pub use label::Label;
|
||||||
|
|
||||||
pub mod enums;
|
pub mod enums;
|
||||||
pub use enums::TextAlign;
|
pub use enums::{LineBreakMode, TextAlign};
|
||||||
|
|
||||||
pub mod font;
|
pub mod font;
|
||||||
pub use font::Font;
|
pub use font::Font;
|
||||||
|
|
Loading…
Reference in a new issue