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,
|
||||
-1001 => ModalResponse::Aborted,
|
||||
-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); }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,7 @@ pub mod listview;
|
|||
pub mod networking;
|
||||
pub mod notification_center;
|
||||
pub mod pasteboard;
|
||||
pub mod progress;
|
||||
pub mod scrollview;
|
||||
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;
|
||||
pub use app::*;
|
||||
|
||||
mod cursor;
|
||||
pub use cursor::{Cursor, CursorType};
|
||||
|
||||
pub mod menu;
|
||||
pub mod printing;
|
||||
pub mod toolbar;
|
||||
|
|
|
@ -219,6 +219,15 @@ extern fn did_expose<T: WindowDelegate>(this: &Object, _: Sel, _: id) {
|
|||
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
|
||||
/// need to do.
|
||||
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!(windowDidExpose:), did_expose::<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();
|
||||
});
|
||||
|
|
|
@ -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
|
||||
/// to your supplied delegate.
|
||||
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 window_controller_class = register_window_controller_class::<T>();
|
||||
|
@ -67,7 +67,7 @@ impl<T> WindowController<T> where T: WindowDelegate + 'static {
|
|||
ShareId::from_ptr(controller)
|
||||
};
|
||||
|
||||
if let Some(delegate) = &window.delegate {
|
||||
if let Some(delegate) = &mut window.delegate {
|
||||
(*delegate).did_load(Window {
|
||||
delegate: None,
|
||||
objc: window.objc.clone()
|
||||
|
|
|
@ -10,10 +10,12 @@
|
|||
|
||||
use std::unreachable;
|
||||
|
||||
use block::ConcreteBlock;
|
||||
|
||||
use core_graphics::base::CGFloat;
|
||||
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_id::ShareId;
|
||||
|
||||
|
@ -66,6 +68,11 @@ impl Window {
|
|||
/// after we initialize the backing `NSWindow`.
|
||||
pub fn new(config: WindowConfig) -> Window {
|
||||
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];
|
||||
|
||||
// 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
|
||||
/// architectures... just without the subclassing.
|
||||
pub fn with(config: WindowConfig, delegate: T) -> Self {
|
||||
let delegate = Box::new(delegate);
|
||||
let mut delegate = Box::new(delegate);
|
||||
|
||||
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];
|
||||
|
||||
// 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,
|
||||
objc: objc.clone()
|
||||
});
|
||||
|
@ -447,6 +459,37 @@ impl<T> Window<T> {
|
|||
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> {
|
||||
|
|
|
@ -13,7 +13,7 @@ pub trait WindowDelegate {
|
|||
/// to set up your views and what not.
|
||||
///
|
||||
/// 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
|
||||
/// 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.
|
||||
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
|
||||
/// 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 {
|
||||
|
@ -32,11 +37,35 @@ impl From<&ThumbnailQuality> for NSUInteger {
|
|||
ThumbnailQuality::Icon => 1 << 0,
|
||||
ThumbnailQuality::Low => 1 << 1,
|
||||
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)]
|
||||
pub struct ThumbnailConfig {
|
||||
|
|
|
@ -7,7 +7,7 @@ use block::ConcreteBlock;
|
|||
use url::Url;
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::foundation::{id, NSUInteger};
|
||||
use crate::foundation::{id, nil, NSUInteger};
|
||||
use crate::image::Image;
|
||||
|
||||
mod config;
|
||||
|
@ -25,13 +25,18 @@ impl ThumbnailGenerator {
|
|||
|
||||
pub fn generate<F>(&self, url: &Url, config: ThumbnailConfig, callback: F)
|
||||
where
|
||||
//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| {
|
||||
unsafe {
|
||||
let image = Image::with(msg_send![thumbnail, NSImage]);
|
||||
callback(Ok((image, ThumbnailQuality::Low)));
|
||||
if error == nil {
|
||||
unsafe {
|
||||
let image = Image::with(msg_send![thumbnail, NSImage]);
|
||||
let quality = ThumbnailQuality::from(thumbnail_type);
|
||||
callback(Ok((image, ThumbnailQuality::Low)));
|
||||
}
|
||||
} else {
|
||||
let error = Error::new(error);
|
||||
callback(Err(error));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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::{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::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||
use crate::text::{Font, TextAlign};
|
||||
use crate::text::{Font, TextAlign, LineBreakMode};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
mod macos;
|
||||
|
@ -237,6 +237,15 @@ impl<T> Label<T> {
|
|||
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> {
|
||||
|
|
|
@ -5,7 +5,7 @@ pub mod label;
|
|||
pub use label::Label;
|
||||
|
||||
pub mod enums;
|
||||
pub use enums::TextAlign;
|
||||
pub use enums::{LineBreakMode, TextAlign};
|
||||
|
||||
pub mod font;
|
||||
pub use font::Font;
|
||||
|
|
Loading…
Reference in a new issue