winio-ui-android 0.1.0

Android backend for winio.
use std::sync::Arc;

use inherit_methods_macro::inherit_methods;
use jni::{
    Env,
    objects::JObject,
    refs::{LoaderContext, Reference},
};
use jni_min_helper::DynamicProxy;
use winio_callback::SyncCallback;
use winio_handle::{AsContainer, AsWidget, BorrowedWidget, impl_as_widget};
use winio_primitive::{Point, Size};

use crate::{
    BaseWidget, JCharSequenceExt, Result, current_activity,
    java::{
        android::{
            view::{View as AView, ViewOnClickListener},
            widget::{Button as AButton, CompoundButton as ACompoundButton, TextView as ATextView},
        },
        material::{CheckBox as ACheckBox, MaterialButton, RadioButton as ARadioButton},
    },
    vm_exec,
};

#[derive(Debug)]
struct ButtonImpl<T>
where
    T: Into<JObject<'static>>
        + AsRef<JObject<'static>>
        + AsRef<AView<'static>>
        + Default
        + Reference
        + Send
        + Sync
        + 'static,
{
    inner: BaseWidget<T>,
    on_click: Arc<SyncCallback>,
    #[allow(dead_code)]
    click_proxy: DynamicProxy,
}

impl<T> ButtonImpl<T>
where
    T: Into<JObject<'static>>
        + AsRef<JObject<'static>>
        + AsRef<AView<'static>>
        + AsRef<AButton<'static>>
        + Default
        + Reference
        + Send
        + Sync
        + 'static,
{
    pub fn new<'any_local, O>(env: &mut Env, parent: impl AsContainer, widget: O) -> Result<Self>
    where
        O: Reference<GlobalKind = T> + AsRef<JObject<'any_local>> + AsRef<AButton<'any_local>>,
    {
        let on_click = Arc::new(SyncCallback::new());
        let click_proxy = DynamicProxy::build(
            env,
            &LoaderContext::None,
            [ViewOnClickListener::class_name()],
            {
                let on_click = on_click.clone();
                move |_env, _method, _args| {
                    on_click.signal(());
                    Ok(JObject::null())
                }
            },
        )?;
        AsRef::<AButton>::as_ref(&widget).set_on_click_listener(env, &click_proxy)?;
        let inner = BaseWidget::new_with_env(env, parent.as_container(), widget)?;
        Ok(Self {
            inner,
            on_click,
            click_proxy,
        })
    }
}

#[inherit_methods(from = "self.inner")]
impl<T> ButtonImpl<T>
where
    T: Into<JObject<'static>>
        + AsRef<JObject<'static>>
        + AsRef<AView<'static>>
        + Default
        + Reference
        + Send
        + Sync
        + 'static,
{
    pub fn is_visible(&self) -> Result<bool>;

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

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

    pub fn set_enabled(&mut self, enabled: 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<()>;

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

    pub fn tooltip(&self) -> Result<String>;

    pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;

    pub async fn wait_click(&self) {
        self.on_click.wait().await;
    }
}

impl<T> ButtonImpl<T>
where
    T: Into<JObject<'static>>
        + AsRef<JObject<'static>>
        + AsRef<AView<'static>>
        + AsRef<ATextView<'static>>
        + Default
        + Reference
        + Send
        + Sync
        + 'static,
{
    fn as_text_view(&self) -> &ATextView<'static> {
        self.inner.as_ref()
    }

    pub fn text(&self) -> Result<String> {
        vm_exec(move |env| Ok(self.as_text_view().get_text(env)?.try_to_string(env)?))
    }

    pub fn set_text(&mut self, text: impl AsRef<str>) -> Result<()> {
        vm_exec(move |env| {
            let text = env.new_string(&text)?;
            self.as_text_view().set_text(env, text)?;
            Ok(())
        })
    }
}

impl<T> ButtonImpl<T>
where
    T: Into<JObject<'static>>
        + AsRef<JObject<'static>>
        + AsRef<AView<'static>>
        + AsRef<ACompoundButton<'static>>
        + Default
        + Reference
        + Send
        + Sync
        + 'static,
{
    fn as_compound_button(&self) -> &ACompoundButton<'static> {
        self.inner.as_ref()
    }

    pub fn is_checked(&self) -> Result<bool> {
        vm_exec(|env| Ok(self.as_compound_button().is_checked(env)?))
    }

    pub fn set_checked(&mut self, checked: bool) -> Result<()> {
        vm_exec(|env| {
            self.as_compound_button().set_checked(env, checked)?;
            Ok(())
        })
    }
}

impl<T> AsWidget for ButtonImpl<T>
where
    T: Into<JObject<'static>>
        + AsRef<JObject<'static>>
        + AsRef<AView<'static>>
        + Default
        + Reference
        + Send
        + Sync
        + 'static,
{
    fn as_widget(&self) -> BorrowedWidget<'_> {
        self.inner.as_widget()
    }
}

#[derive(Debug)]
pub struct Button {
    inner: ButtonImpl<MaterialButton<'static>>,
}

#[inherit_methods(from = "self.inner")]
impl Button {
    pub fn new(parent: impl AsContainer) -> Result<Self> {
        vm_exec(|env| {
            let act = current_activity(env)?;
            let widget = MaterialButton::new(env, act)?;
            let inner = ButtonImpl::new(env, parent, widget)?;
            Ok(Self { inner })
        })
    }

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

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

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

    pub fn set_enabled(&mut self, enabled: 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<()>;

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

    pub fn tooltip(&self) -> Result<String>;

    pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;

    pub fn text(&self) -> Result<String>;

    pub fn set_text(&mut self, text: impl AsRef<str>) -> Result<()>;

    pub async fn wait_click(&self) {
        self.inner.wait_click().await;
    }
}

impl_as_widget!(Button, inner);

#[derive(Debug)]
pub struct CheckBox {
    inner: ButtonImpl<ACheckBox<'static>>,
}

#[inherit_methods(from = "self.inner")]
impl CheckBox {
    pub fn new(parent: impl AsContainer) -> Result<Self> {
        vm_exec(|env| {
            let act = current_activity(env)?;
            let widget = ACheckBox::new(env, act)?;
            let inner = ButtonImpl::new(env, parent, widget)?;
            Ok(Self { inner })
        })
    }

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

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

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

    pub fn set_enabled(&mut self, enabled: 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<()>;

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

    pub fn tooltip(&self) -> Result<String>;

    pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;

    pub fn text(&self) -> Result<String>;

    pub fn set_text(&mut self, text: impl AsRef<str>) -> Result<()>;

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

    pub fn set_checked(&mut self, checked: bool) -> Result<()>;

    pub async fn wait_click(&self) {
        self.inner.wait_click().await;
    }
}

impl_as_widget!(CheckBox, inner);

#[derive(Debug)]
pub struct RadioButton {
    inner: ButtonImpl<ARadioButton<'static>>,
}

#[inherit_methods(from = "self.inner")]
impl RadioButton {
    pub fn new(parent: impl AsContainer) -> Result<Self> {
        vm_exec(|env| {
            let act = current_activity(env)?;
            let widget = ARadioButton::new(env, act)?;
            let inner = ButtonImpl::new(env, parent, widget)?;
            Ok(Self { inner })
        })
    }

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

    pub fn set_visible(&mut self, visible: 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<()>;

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

    pub fn tooltip(&self) -> Result<String>;

    pub fn set_tooltip(&mut self, s: impl AsRef<str>) -> Result<()>;

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

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

    pub fn text(&self) -> Result<String>;

    pub fn set_text(&mut self, s: impl AsRef<str>) -> Result<()>;

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

    pub fn set_checked(&mut self, _v: bool) -> Result<()>;

    pub async fn wait_click(&self) {
        self.inner.wait_click().await
    }
}

impl_as_widget!(RadioButton, inner);