Begin moving certain core methods to Layout.
- Drag and drop, hidden, and so on now live on on the `Layout` trait, which helps make the logic less of a hassle to support (these methods are almost always guaranteed to exist on any view type, and it's easier to just noop them on a specific view if need be). - Begin reworking auto-drop-remove-from-superview logic by including an `is_handle` flag on `View`; will need to extend this work to others.
This commit is contained in:
parent
46ee9e2ea8
commit
1b0be74fc8
|
@ -248,12 +248,20 @@ impl Layout for Button {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for &Button {
|
impl Layout for &Button {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Button {
|
impl Drop for Button {
|
||||||
|
|
|
@ -117,21 +117,16 @@ impl ImageView {
|
||||||
let _: () = msg_send![obj, setImage:&*image.0];
|
let _: () = msg_send![obj, setImage:&*image.0];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_hidden(&self, hidden: bool) {
|
|
||||||
self.objc.with_mut(|obj| unsafe {
|
|
||||||
let _: () = msg_send![obj, setHidden:match hidden {
|
|
||||||
true => YES,
|
|
||||||
false => NO
|
|
||||||
}];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layout for ImageView {
|
impl Layout for ImageView {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ImageView {
|
impl Drop for ImageView {
|
||||||
|
|
|
@ -257,6 +257,10 @@ impl<T> Layout for TextField<T> {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for TextField<T> {
|
impl<T> Drop for TextField<T> {
|
||||||
|
|
|
@ -7,17 +7,34 @@ use objc::{msg_send, sel, sel_impl};
|
||||||
use objc::runtime::Object;
|
use objc::runtime::Object;
|
||||||
use objc_id::ShareId;
|
use objc_id::ShareId;
|
||||||
|
|
||||||
use crate::foundation::{id, YES, NO};
|
use crate::foundation::{id, nil, to_bool, YES, NO, NSArray, NSString};
|
||||||
use crate::geometry::Rect;
|
use crate::geometry::Rect;
|
||||||
|
use crate::pasteboard::PasteboardType;
|
||||||
|
|
||||||
/// A trait that view wrappers must conform to. Enables managing the subview tree.
|
/// A trait that view wrappers must conform to. Enables managing the subview tree.
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub trait Layout {
|
pub trait Layout {
|
||||||
/// Returns a reference to the backing Objective-C layer. This is optional, as we try to keep
|
/// Used for mutably interacting with the underlying Objective-C instance.
|
||||||
/// the general lazy-loading approach Cocoa has. This may change in the future, and in general
|
|
||||||
/// this shouldn't affect your code too much (if at all).
|
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F);
|
fn with_backing_node<F: Fn(id)>(&self, handler: F);
|
||||||
|
|
||||||
|
/// Used for checking backing properties of the underlying Objective-C instance, without
|
||||||
|
/// needing a mutable borrow.
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R;
|
||||||
|
|
||||||
|
/// Sets whether this needs to be redrawn before being displayed.
|
||||||
|
///
|
||||||
|
/// If you're updating data that dynamically impacts this view, mark this as true - the next
|
||||||
|
/// pass from the system will redraw it accordingly, and set the underlying value back to
|
||||||
|
/// `false`.
|
||||||
|
fn set_needs_display(&self, needs_display: bool) {
|
||||||
|
self.with_backing_node(|obj| unsafe {
|
||||||
|
let _: () = msg_send![obj, setNeedsDisplay:match needs_display {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Adds another Layout-backed control or view as a subview of this view.
|
/// Adds another Layout-backed control or view as a subview of this view.
|
||||||
fn add_subview<V: Layout>(&self, view: &V) {
|
fn add_subview<V: Layout>(&self, view: &V) {
|
||||||
self.with_backing_node(|backing_node| {
|
self.with_backing_node(|backing_node| {
|
||||||
|
@ -60,4 +77,82 @@ pub trait Layout {
|
||||||
}];
|
}];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets whether the view for this is hidden or not.
|
||||||
|
///
|
||||||
|
/// When hidden, widgets don't receive events and is not visible.
|
||||||
|
fn set_hidden(&self, hide: bool) {
|
||||||
|
self.with_backing_node(|obj| unsafe {
|
||||||
|
let _: () = msg_send![obj, setHidden:match hide {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this is hidden or not.
|
||||||
|
///
|
||||||
|
/// Note that this can report `false` if an ancestor widget is hidden, thus hiding this - to check in
|
||||||
|
/// that case, you may want `is_hidden_or_ancestor_is_hidden()`.
|
||||||
|
fn is_hidden(&self) -> bool {
|
||||||
|
self.get_from_backing_node(|obj| {
|
||||||
|
to_bool(unsafe {
|
||||||
|
msg_send![obj, isHidden]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether this is hidden, *or* whether an ancestor view is hidden.
|
||||||
|
fn is_hidden_or_ancestor_is_hidden(&self) -> bool {
|
||||||
|
self.get_from_backing_node(|obj| {
|
||||||
|
to_bool(unsafe {
|
||||||
|
msg_send![obj, isHiddenOrHasHiddenAncestor]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register this view for drag and drop operations.
|
||||||
|
fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
||||||
|
let types: NSArray = types.into_iter().map(|t| {
|
||||||
|
let x: NSString = (*t).into();
|
||||||
|
x.into()
|
||||||
|
}).collect::<Vec<id>>().into();
|
||||||
|
|
||||||
|
self.with_backing_node(|obj| unsafe {
|
||||||
|
let _: () = msg_send![obj, registerForDraggedTypes:&*types];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unregisters this as a target for drag and drop operations.
|
||||||
|
fn unregister_dragged_types(&self) {
|
||||||
|
self.with_backing_node(|obj| unsafe {
|
||||||
|
let _: () = msg_send![obj, unregisterDraggedTypes];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether this posts notifications when the frame rectangle changes.
|
||||||
|
///
|
||||||
|
/// If you have a high performance tableview or collectionview that has issues, disabling these
|
||||||
|
/// can be helpful - but always test!
|
||||||
|
fn set_posts_frame_change_notifications(&self, posts: bool) {
|
||||||
|
self.with_backing_node(|obj| unsafe {
|
||||||
|
let _: () = msg_send![obj, setPostsFrameChangedNotifications:match posts {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets whether this posts notifications when the bounds rectangle changes.
|
||||||
|
///
|
||||||
|
/// If you have a high performance tableview or collectionview that has issues, disabling these
|
||||||
|
/// can be helpful - but always test!
|
||||||
|
fn set_posts_bounds_change_notifications(&self, posts: bool) {
|
||||||
|
self.with_backing_node(|obj| unsafe {
|
||||||
|
let _: () = msg_send![obj, setPostsBoundsChangedNotifications:match posts {
|
||||||
|
true => YES,
|
||||||
|
false => NO
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,6 @@ use objc::{class, msg_send, sel, sel_impl};
|
||||||
use crate::foundation::{id, nil, YES, NO, NSArray, NSString, NSInteger, NSUInteger};
|
use crate::foundation::{id, nil, YES, NO, NSArray, NSString, NSInteger, NSUInteger};
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||||
use crate::pasteboard::PasteboardType;
|
|
||||||
use crate::scrollview::ScrollView;
|
use crate::scrollview::ScrollView;
|
||||||
use crate::utils::{os, CellFactory, CGSize};
|
use crate::utils::{os, CellFactory, CGSize};
|
||||||
use crate::utils::properties::{ObjcProperty, PropertyNullable};
|
use crate::utils::properties::{ObjcProperty, PropertyNullable};
|
||||||
|
@ -571,20 +570,6 @@ impl<T> ListView<T> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register this view for drag and drop operations.
|
|
||||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
|
||||||
let types: NSArray = types.into_iter().map(|t| {
|
|
||||||
// This clone probably doesn't need to be here, but it should also be cheap as
|
|
||||||
// this is just an enum... and this is not an oft called method.
|
|
||||||
let x: NSString = (*t).into();
|
|
||||||
x.into()
|
|
||||||
}).collect::<Vec<id>>().into();
|
|
||||||
|
|
||||||
self.objc.with_mut(|obj| unsafe {
|
|
||||||
let _: () = msg_send![obj, registerForDraggedTypes:&*types];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reloads the underlying ListView. This is more expensive than handling insert/reload/remove
|
/// Reloads the underlying ListView. This is more expensive than handling insert/reload/remove
|
||||||
/// calls yourself, but often easier to implement.
|
/// calls yourself, but often easier to implement.
|
||||||
///
|
///
|
||||||
|
@ -634,6 +619,16 @@ impl<T> Layout for ListView<T> {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
self.scrollview.objc.with_mut(handler);
|
self.scrollview.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
// On macOS, we need to provide the scrollview for layout purposes - iOS and tvOS will know
|
||||||
|
// what to do normally.
|
||||||
|
//
|
||||||
|
// @TODO: Review this, as property access isn't really used in the same place as layout
|
||||||
|
// stuff... hmm...
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
self.scrollview.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for ListView<T> {
|
impl<T> Drop for ListView<T> {
|
||||||
|
|
|
@ -52,7 +52,6 @@ use crate::foundation::{id, nil, YES, NO, NSArray, NSString};
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::layer::Layer;
|
use crate::layer::Layer;
|
||||||
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
use crate::layout::{Layout, LayoutAnchorX, LayoutAnchorY, LayoutAnchorDimension};
|
||||||
use crate::pasteboard::PasteboardType;
|
|
||||||
use crate::view::ViewDelegate;
|
use crate::view::ViewDelegate;
|
||||||
use crate::utils::properties::ObjcProperty;
|
use crate::utils::properties::ObjcProperty;
|
||||||
|
|
||||||
|
@ -294,26 +293,16 @@ impl<T> ListViewRow<T> {
|
||||||
(&mut *obj).set_ivar(BACKGROUND_COLOR, color);
|
(&mut *obj).set_ivar(BACKGROUND_COLOR, color);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register this view for drag and drop operations.
|
|
||||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
|
||||||
let types: NSArray = types.into_iter().map(|t| {
|
|
||||||
// This clone probably doesn't need to be here, but it should also be cheap as
|
|
||||||
// this is just an enum... and this is not an oft called method.
|
|
||||||
let x: NSString = (*t).into();
|
|
||||||
x.into()
|
|
||||||
}).collect::<Vec<id>>().into();
|
|
||||||
|
|
||||||
self.objc.with_mut(|obj| unsafe {
|
|
||||||
let _: () = msg_send![obj, registerForDraggedTypes:&*types];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Layout for ListViewRow<T> {
|
impl<T> Layout for ListViewRow<T> {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for ListViewRow<T> {
|
impl<T> Drop for ListViewRow<T> {
|
||||||
|
|
|
@ -176,6 +176,10 @@ impl Layout for ProgressIndicator {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ProgressIndicator {
|
impl Drop for ProgressIndicator {
|
||||||
|
|
|
@ -225,6 +225,10 @@ impl<T> Layout for ScrollView<T> {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for ScrollView<T> {
|
impl<T> Drop for ScrollView<T> {
|
||||||
|
|
|
@ -98,8 +98,10 @@ impl Switch {
|
||||||
/// Attaches a callback for button press events. Don't get too creative now...
|
/// Attaches a callback for button press events. Don't get too creative now...
|
||||||
/// best just to message pass or something.
|
/// best just to message pass or something.
|
||||||
pub fn set_action<F: Fn() + Send + Sync + 'static>(&mut self, action: F) {
|
pub fn set_action<F: Fn() + Send + Sync + 'static>(&mut self, action: F) {
|
||||||
//let handler = TargetActionHandler::new(&*self.objc, action);
|
// @TODO: This probably isn't ideal but gets the job done for now; needs revisiting.
|
||||||
//self.handler = Some(handler);
|
let this = self.objc.get(|obj| unsafe { ShareId::from_ptr(msg_send![obj, self]) });
|
||||||
|
let handler = TargetActionHandler::new(&*this, action);
|
||||||
|
self.handler = Some(handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,10 +109,14 @@ impl Layout for Switch {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
|
|
||||||
fn add_subview<V: Layout>(&self, _view: &V) {
|
fn add_subview<V: Layout>(&self, _view: &V) {
|
||||||
panic!(r#"
|
panic!(r#"
|
||||||
Tried to add a subview to a Button. This is not allowed in Cacao. If you think this should be supported,
|
Tried to add a subview to a Switch. This is not allowed in Cacao. If you think this should be supported,
|
||||||
open a discussion on the GitHub repo.
|
open a discussion on the GitHub repo.
|
||||||
"#);
|
"#);
|
||||||
}
|
}
|
||||||
|
|
|
@ -345,6 +345,10 @@ impl<T> Layout for Label<T> {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for Label<T> {
|
impl<T> Drop for Label<T> {
|
||||||
|
|
|
@ -232,23 +232,16 @@ impl<T> View<T> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register this view for drag and drop operations.
|
|
||||||
pub fn register_for_dragged_types(&self, types: &[PasteboardType]) {
|
|
||||||
let types: NSArray = types.into_iter().map(|t| {
|
|
||||||
let x: NSString = (*t).into();
|
|
||||||
x.into()
|
|
||||||
}).collect::<Vec<id>>().into();
|
|
||||||
|
|
||||||
self.objc.with_mut(|obj| unsafe {
|
|
||||||
let _: () = msg_send![obj, registerForDraggedTypes:&*types];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Layout for View<T> {
|
impl<T> Layout for View<T> {
|
||||||
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
self.objc.with_mut(handler);
|
self.objc.with_mut(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
self.objc.get(handler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Drop for View<T> {
|
impl<T> Drop for View<T> {
|
||||||
|
|
|
@ -211,9 +211,14 @@ impl<T> WebView<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Layout for WebView<T> {
|
impl<T> Layout for WebView<T> {
|
||||||
/// Returns the Objective-C object used for handling the view heirarchy.
|
fn with_backing_node<F: Fn(id)>(&self, handler: F) {
|
||||||
fn get_backing_node(&self) -> ShareId<Object> {
|
// @TODO: Fix.
|
||||||
self.objc.clone()
|
//self.objc.with_mut(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_from_backing_node<F: Fn(&Object) -> R, R>(&self, handler: F) -> R {
|
||||||
|
// @TODO: Fix.
|
||||||
|
//self.objc.get(handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Currently, this is a noop. Theoretically there is reason to support this, but in practice
|
/// Currently, this is a noop. Theoretically there is reason to support this, but in practice
|
||||||
|
|
Loading…
Reference in a new issue