winio-ui-app-kit 0.6.0

AppKit backend for winio.
Documentation
use inherit_methods_macro::inherit_methods;
use objc2::{MainThreadOnly, rc::Retained};
use objc2_app_kit::{NSControl, NSScrollView, NSView};
use objc2_foundation::NSSize;
use winio_handle::{AsContainer, BorrowedContainer};
use winio_primitive::{Point, Size};

use crate::{Result, catch, from_cgsize, transform_cgrect, transform_rect, widgets::Widget};

#[derive(Debug)]
pub struct ScrollView {
    handle: Widget,
    view: Retained<NSScrollView>,
    inner_view: Retained<NSView>,
}

#[inherit_methods(from = "self.handle")]
impl ScrollView {
    pub fn new(parent: impl AsContainer) -> Result<Self> {
        let parent = parent.as_container();
        let mtm = parent.as_app_kit().mtm();

        catch(|| unsafe {
            let view = NSScrollView::new(mtm);
            let inner_view = NSView::new(mtm);
            let handle = Widget::from_nsview(parent, Retained::cast_unchecked(view.clone()))?;

            view.setHasVerticalScroller(true);
            view.setHasHorizontalScroller(true);
            view.setDocumentView(Some(&inner_view));

            Ok(Self {
                handle,
                view,
                inner_view,
            })
        })
        .flatten()
    }

    pub fn is_visible(&self) -> Result<bool>;

    pub fn set_visible(&mut self, v: bool) -> Result<()>;

    pub fn is_enabled(&self) -> Result<bool>;

    pub fn set_enabled(&mut self, v: bool) -> Result<()>;

    pub fn loc(&self) -> Result<Point>;

    pub fn set_loc(&mut self, p: Point) -> Result<()>;

    pub fn size(&self) -> Result<Size>;

    pub fn set_size(&mut self, v: Size) -> Result<()> {
        self.handle.set_size(v)?;
        catch(|| {
            let inner_size = from_cgsize(self.inner_view.frame().size);
            let subviews = self.inner_view.subviews();
            let frames = subviews
                .iter()
                .map(|c| {
                    let mut frame = c.frame();
                    let size = {
                        let size = c.fittingSize();
                        if size != NSSize::ZERO {
                            size
                        } else if let Ok(c) = c.downcast::<NSControl>() {
                            c.sizeThatFits(NSSize::ZERO)
                        } else {
                            NSSize::ZERO
                        }
                    };
                    frame.size.width = frame.size.width.max(size.width);
                    frame.size.height = frame.size.height.max(size.height);
                    transform_cgrect(inner_size, frame)
                })
                .collect::<Vec<_>>();
            let rect = frames
                .iter()
                .copied()
                .reduce(|a, b| a.union(&b))
                .unwrap_or_default();
            let mut rect = rect.to_box2d();
            rect.min = rect.min.min(Point::zero());
            let mut rect = rect.to_rect();
            if rect.height() < v.height {
                rect.size.height = v.height;
            }
            let frame = transform_rect(v, rect);
            self.inner_view.setFrame(frame);
            let inner_size = from_cgsize(frame.size);
            for (c, rect) in subviews.into_iter().zip(frames) {
                c.setFrame(transform_rect(inner_size, rect));
            }
        })
    }

    pub fn hscroll(&self) -> Result<bool> {
        catch(|| self.view.hasHorizontalScroller())
    }

    pub fn set_hscroll(&mut self, v: bool) -> Result<()> {
        catch(|| self.view.setHasHorizontalScroller(v))
    }

    pub fn vscroll(&self) -> Result<bool> {
        catch(|| self.view.hasVerticalScroller())
    }

    pub fn set_vscroll(&mut self, v: bool) -> Result<()> {
        catch(|| self.view.setHasVerticalScroller(v))
    }

    pub async fn start(&self) -> ! {
        std::future::pending().await
    }
}

winio_handle::impl_as_widget!(ScrollView, handle);

impl AsContainer for ScrollView {
    fn as_container(&self) -> BorrowedContainer<'_> {
        BorrowedContainer::app_kit(&self.inner_view)
    }
}