winio-ui-android 0.2.0-beta.2

Android backend for winio.
use jni::refs::Global;
use winio_handle::{AsContainer, AsWidget, BorrowedWidget};
use winio_primitive::{Point, Size};

use crate::{
    BaseWidget, Result, current_activity,
    java::android::{
        view::ViewGroup as AViewGroup,
        widget::{
            FrameLayout, HorizontalScrollView as AHorizontalScrollView, ScrollView as AScrollView,
        },
    },
    vm_exec,
};

#[derive(Debug)]
pub struct ScrollView {
    vertical: BaseWidget<AScrollView<'static>>,
    horizontal: BaseWidget<AHorizontalScrollView<'static>>,
    inner_view: BaseWidget<FrameLayout<'static>>,
    enable_vertical: bool,
    enable_horizontal: bool,
}

impl ScrollView {
    pub fn new(parent: impl AsContainer) -> Result<Self> {
        vm_exec(|env| {
            let context = current_activity(env)?;
            let vertical = AScrollView::new(env, &context)?;
            let horizontal = AHorizontalScrollView::new(env, &context)?;
            let inner_view = FrameLayout::new(env, &context)?;
            vertical.as_view_group().add_view(env, &horizontal)?;
            horizontal.as_view_group().add_view(env, &inner_view)?;
            let vertical = BaseWidget::new_with_env(env, parent.as_container(), vertical)?;
            let horizontal = env.new_global_ref(horizontal)?.into();
            let inner_view = env.new_global_ref(inner_view)?.into();
            Ok(Self {
                vertical,
                horizontal,
                inner_view,
                enable_vertical: true,
                enable_horizontal: true,
            })
        })
    }

    pub fn is_visible(&self) -> Result<bool> {
        self.inner_view.is_visible()
    }

    pub fn set_visible(&mut self, v: bool) -> Result<()> {
        self.inner_view.set_visible(v)
    }

    pub fn is_enabled(&self) -> Result<bool> {
        self.inner_view.is_enabled()
    }

    pub fn set_enabled(&mut self, v: bool) -> Result<()> {
        self.inner_view.set_enabled(v)
    }

    pub fn loc(&self) -> Result<Point> {
        if self.enable_vertical {
            self.vertical.loc()
        } else if self.enable_horizontal {
            self.horizontal.loc()
        } else {
            self.inner_view.loc()
        }
    }

    pub fn set_loc(&mut self, p: Point) -> Result<()> {
        if self.enable_vertical {
            self.vertical.set_loc(p)?;
            self.horizontal.set_loc(Point::zero())?;
            self.inner_view.set_loc(Point::zero())?;
        } else if self.enable_horizontal {
            self.horizontal.set_loc(p)?;
            self.inner_view.set_loc(Point::zero())?;
        } else {
            self.inner_view.set_loc(p)?;
        }
        Ok(())
    }

    pub fn size(&self) -> Result<Size> {
        if self.enable_vertical {
            self.vertical.size()
        } else if self.enable_horizontal {
            self.horizontal.size()
        } else {
            self.inner_view.size()
        }
    }

    pub fn set_size(&mut self, v: Size) -> Result<()> {
        if self.enable_vertical {
            self.vertical.set_size(v)?;
            self.horizontal.set_wrap_content()?;
            self.inner_view.set_wrap_content()?;
        } else if self.enable_horizontal {
            self.horizontal.set_size(v)?;
            self.inner_view.set_wrap_content()?;
        } else {
            self.inner_view.set_size(v)?;
        }
        Ok(())
    }

    fn detach(&mut self) -> Result<Option<Global<AViewGroup<'static>>>> {
        vm_exec(|env| {
            let root = if self.enable_vertical {
                self.vertical.as_view()
            } else if self.enable_horizontal {
                self.horizontal.as_view()
            } else {
                self.inner_view.as_view()
            };
            let parent = root.get_parent(env)?;
            if parent.is_null() {
                return Ok(None);
            }
            let parent = unsafe { AViewGroup::from_raw(env, parent.into_raw()) };
            let parent = env.new_global_ref(&parent)?;
            parent.remove_view(env, root)?;
            if self.enable_vertical {
                if self.enable_horizontal {
                    // V > H > I
                    self.vertical
                        .as_view_group()
                        .remove_view(env, self.horizontal.as_view())?;
                    self.horizontal
                        .as_view_group()
                        .remove_view(env, self.inner_view.as_view())?;
                } else {
                    // V > I
                    self.vertical
                        .as_view_group()
                        .remove_view(env, self.inner_view.as_view())?;
                }
            } else if self.enable_horizontal {
                // H > I
                self.horizontal
                    .as_view_group()
                    .remove_view(env, self.inner_view.as_view())?;
            }
            Ok(Some(parent))
        })
    }

    fn attach(&mut self, parent: &Global<AViewGroup<'static>>) -> Result<()> {
        vm_exec(|env| {
            if self.enable_vertical {
                parent.add_view(env, self.vertical.as_view())?;
                if self.enable_horizontal {
                    // V > H > I
                    self.vertical
                        .as_view_group()
                        .add_view(env, self.horizontal.as_view())?;
                    self.horizontal
                        .as_view_group()
                        .add_view(env, self.inner_view.as_view())?;
                } else {
                    // V > I
                    self.vertical
                        .as_view_group()
                        .add_view(env, self.inner_view.as_view())?;
                }
            } else if self.enable_horizontal {
                // H > I
                parent.add_view(env, self.horizontal.as_view())?;
                self.horizontal
                    .as_view_group()
                    .add_view(env, self.inner_view.as_view())?;
            } else {
                // I
                parent.add_view(env, self.inner_view.as_view())?;
            }
            Ok(())
        })
    }

    pub fn hscroll(&self) -> Result<bool> {
        Ok(self.enable_horizontal)
    }

    pub fn set_hscroll(&mut self, v: bool) -> Result<()> {
        if self.enable_horizontal != v {
            let parent = self.detach()?;
            self.enable_horizontal = v;
            if let Some(parent) = &parent {
                self.attach(parent)?;
            }
        }
        Ok(())
    }

    pub fn vscroll(&self) -> Result<bool> {
        Ok(self.enable_vertical)
    }

    pub fn set_vscroll(&mut self, v: bool) -> Result<()> {
        if self.enable_vertical != v {
            let parent = self.detach()?;
            self.enable_vertical = v;
            if let Some(parent) = &parent {
                self.attach(parent)?;
            }
        }
        Ok(())
    }

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

winio_handle::impl_as_container!(ScrollView, inner_view);

impl AsWidget for ScrollView {
    fn as_widget(&self) -> BorrowedWidget<'_> {
        if self.enable_vertical {
            self.vertical.as_widget()
        } else if self.enable_horizontal {
            self.horizontal.as_widget()
        } else {
            self.inner_view.as_widget()
        }
    }
}