use crate::{
helper::{as_ref, chars_to_string, to_c_string},
task::{BackgroundPtr, Task},
try_gp_internal, Camera, Error, Result,
};
use std::{
ffi, fmt,
ops::{Range, RangeInclusive},
os::raw::{c_char, c_int, c_void},
};
pub struct WidgetIterator<'a> {
parent_widget: &'a GroupWidget,
range: Range<usize>,
}
impl Iterator for WidgetIterator<'_> {
type Item = Widget;
fn next(&mut self) -> Option<Self::Item> {
self.range.next().map(|i| self.parent_widget.get_child(i).unwrap())
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
}
impl ExactSizeIterator for WidgetIterator<'_> {
fn len(&self) -> usize {
self.range.len()
}
}
pub struct WidgetBase {
pub(crate) inner: BackgroundPtr<libgphoto2_sys::CameraWidget>,
}
impl Clone for WidgetBase {
fn clone(&self) -> Self {
try_gp_internal!(gp_widget_ref(*self.inner).unwrap());
Self { inner: self.inner }
}
}
impl Drop for WidgetBase {
fn drop(&mut self) {
let widget_ptr = self.inner;
unsafe {
Task::new(move || {
try_gp_internal!(gp_widget_unref(*widget_ptr).unwrap());
})
}
.background();
}
}
impl WidgetBase {
fn as_ptr(&self) -> *mut libgphoto2_sys::CameraWidget {
*self.inner
}
fn ty(&self) -> libgphoto2_sys::CameraWidgetType {
try_gp_internal!(gp_widget_get_type(*self.inner, &out widget_type).unwrap());
widget_type
}
pub fn readonly(&self) -> bool {
try_gp_internal!(gp_widget_get_readonly(*self.inner, &out readonly).unwrap());
readonly == 1
}
pub fn label(&self) -> String {
try_gp_internal!(gp_widget_get_label(*self.inner, &out label).unwrap());
chars_to_string(label)
}
pub fn name(&self) -> String {
try_gp_internal!(gp_widget_get_name(*self.inner, &out name).unwrap());
chars_to_string(name)
}
pub fn id(&self) -> i32 {
try_gp_internal!(gp_widget_get_id(*self.inner, &out id).unwrap());
id
}
pub fn info(&self) -> String {
try_gp_internal!(gp_widget_get_info(*self.inner, &out info).unwrap());
chars_to_string(info)
}
fn fmt_fields(&self, f: &mut fmt::DebugStruct) {
f.field("id", &self.id())
.field("name", &self.name())
.field("label", &self.label())
.field("readonly", &self.readonly());
}
unsafe fn raw_value<T>(&self) -> T {
let mut value = std::mem::MaybeUninit::<T>::uninit();
try_gp_internal!(gp_widget_get_value(*self.inner, value.as_mut_ptr().cast::<c_void>()).unwrap());
value.assume_init()
}
unsafe fn set_raw_value<T>(&self, value: *const T) {
try_gp_internal!(gp_widget_set_value(*self.inner, value.cast::<c_void>()).unwrap());
}
}
impl fmt::Debug for WidgetBase {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("WidgetBase");
f.field("type", &self.ty());
self.fmt_fields(&mut f);
f.finish()
}
}
as_ref!(WidgetBase -> libgphoto2_sys::CameraWidget, **self.inner);
macro_rules! join_strings {
($delim:literal, $first:expr $(, $rest:expr)*) => {
concat!($first $(, $delim, $rest)*)
};
}
macro_rules! typed_widgets {
($($name:ident, $variant:ident = $($gp_name:ident)|+;)*) => {
#[derive(Clone)]
pub enum Widget {
$(
#[doc = concat!("Variant representing a [`", stringify!($name), "`].")]
$variant($name),
)*
}
impl Widget {
pub(crate) fn new_owned(widget: BackgroundPtr<libgphoto2_sys::CameraWidget>) -> Self {
let inner = WidgetBase { inner: widget };
match inner.ty() {
$($(libgphoto2_sys::CameraWidgetType::$gp_name)|+ => Widget::$variant($name { inner }),)*
}
}
}
$(
impl From<$name> for Widget {
fn from(widget: $name) -> Self {
Widget::$variant(widget)
}
}
impl TryFrom<Widget> for $name {
type Error = Error;
fn try_from(widget: Widget) -> Result<Self> {
match widget {
Widget::$variant(widget) => Ok(widget),
_ => Err(Error::from(format!("Expected {} but got {:?}", stringify!($name), widget))),
}
}
}
)*
impl Widget {
pub fn try_into<T>(self) -> std::result::Result<T, <T as TryFrom<Widget>>::Error>
where
T: TryFrom<Widget>,
{
TryInto::try_into(self)
}
}
impl fmt::Debug for Widget {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$(Self::$variant(widget) => widget.fmt(f),)*
}
}
}
impl std::ops::Deref for Widget {
type Target = WidgetBase;
fn deref(&self) -> &WidgetBase {
match self {
$(Self::$variant(widget) => widget),*
}
}
}
$(
#[doc = concat!(
"Widget representing ",
join_strings!(
" or "
$(, concat!(
"[`", stringify!($gp_name), "`]",
"(libgphoto2_sys::CameraWidgetType::", stringify!($gp_name), ")"
))+
),
"."
)]
#[derive(Clone)]
pub struct $name {
inner: WidgetBase,
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut f = f.debug_struct(stringify!($name));
self.inner.fmt_fields(&mut f);
$name::fmt_fields(self, &mut f);
f.finish()
}
}
impl std::ops::Deref for $name {
type Target = WidgetBase;
fn deref(&self) -> &WidgetBase {
&self.inner
}
}
)*
};
}
typed_widgets!(
GroupWidget, Group = GP_WIDGET_WINDOW | GP_WIDGET_SECTION;
TextWidget, Text = GP_WIDGET_TEXT;
RangeWidget, Range = GP_WIDGET_RANGE;
ToggleWidget, Toggle = GP_WIDGET_TOGGLE;
RadioWidget, Radio = GP_WIDGET_MENU | GP_WIDGET_RADIO;
ButtonWidget, Button = GP_WIDGET_BUTTON;
DateWidget, Date = GP_WIDGET_DATE;
);
struct MaybeListFmt<F>(F);
impl<Iter: IntoIterator, F: Fn() -> Iter> fmt::Debug for MaybeListFmt<F>
where
Iter::IntoIter: ExactSizeIterator,
Iter::Item: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let list = (self.0)();
if f.alternate() {
f.debug_list().entries(list).finish()
} else {
write!(f, "[_; {}]", list.into_iter().len())
}
}
}
impl GroupWidget {
pub fn children_iter(&self) -> WidgetIterator<'_> {
WidgetIterator { parent_widget: self, range: 0..self.children_count() }
}
pub fn children_count(&self) -> usize {
try_gp_internal!(let count = gp_widget_count_children(self.as_ptr()).unwrap());
count.try_into().unwrap()
}
pub fn get_child(&self, index: usize) -> Result<Widget> {
try_gp_internal!(gp_widget_get_child(self.as_ptr(), index.try_into()?, &out child)?);
Ok(Widget::new_shared(BackgroundPtr(child)))
}
pub fn get_child_by_id(&self, id: usize) -> Result<Widget> {
try_gp_internal!(gp_widget_get_child_by_id(self.as_ptr(), id.try_into()?, &out child)?);
Ok(Widget::new_shared(BackgroundPtr(child)))
}
pub fn get_child_by_label(&self, label: &str) -> Result<Widget> {
try_gp_internal!(gp_widget_get_child_by_label(self.as_ptr(), to_c_string!(label), &out child)?);
Ok(Widget::new_shared(BackgroundPtr(child)))
}
pub fn get_child_by_name(&self, name: &str) -> Result<Widget> {
try_gp_internal!(gp_widget_get_child_by_name(self.as_ptr(), to_c_string!(name), &out child)?);
Ok(Widget::new_shared(BackgroundPtr(child)))
}
fn fmt_fields(&self, f: &mut fmt::DebugStruct) {
f.field("children", &MaybeListFmt(|| self.children_iter()));
}
}
impl TextWidget {
pub fn value(&self) -> String {
chars_to_string(unsafe { self.raw_value::<*const c_char>() })
}
pub fn set_value(&self, value: &str) -> Result<()> {
unsafe {
self.set_raw_value::<c_char>(to_c_string!(value));
}
Ok(())
}
fn fmt_fields(&self, f: &mut fmt::DebugStruct) {
f.field("value", &self.value());
}
}
impl RangeWidget {
pub fn value(&self) -> f32 {
unsafe { self.raw_value::<f32>() }
}
pub fn set_value(&self, value: f32) {
unsafe { self.set_raw_value::<f32>(&value) }
}
pub fn range_and_step(&self) -> (RangeInclusive<f32>, f32) {
try_gp_internal!(gp_widget_get_range(self.as_ptr(), &out min, &out max, &out step).unwrap());
(min..=max, step)
}
fn fmt_fields(&self, f: &mut fmt::DebugStruct) {
let (range, step) = self.range_and_step();
f.field("range", &range).field("step", &step).field("value", &self.value());
}
}
impl ToggleWidget {
pub fn toggled(&self) -> Option<bool> {
let value = unsafe { self.raw_value::<c_int>() };
match value {
0 => Some(false),
1 => Some(true),
_ => None,
}
}
pub fn set_toggled(&self, value: bool) {
unsafe { self.set_raw_value::<c_int>(&value.into()) }
}
fn fmt_fields(&self, f: &mut fmt::DebugStruct) {
f.field("toggled", &self.toggled());
}
}
pub struct ChoicesIter<'a> {
widget: &'a RadioWidget,
range: Range<c_int>,
}
impl<'a> Iterator for ChoicesIter<'a> {
type Item = String;
fn next(&mut self) -> Option<Self::Item> {
self.range.next().map(|i| {
try_gp_internal!(gp_widget_get_choice(self.widget.as_ptr(), i, &out choice).unwrap());
chars_to_string(choice)
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.range.size_hint()
}
}
impl ExactSizeIterator for ChoicesIter<'_> {
fn len(&self) -> usize {
self.range.len()
}
}
impl RadioWidget {
pub fn choices_iter(&self) -> ChoicesIter<'_> {
try_gp_internal!(let choice_count = gp_widget_count_choices(self.as_ptr()).unwrap());
ChoicesIter { widget: self, range: 0..choice_count }
}
pub fn choice(&self) -> String {
chars_to_string(unsafe { self.raw_value::<*const c_char>() })
}
pub fn set_choice(&self, value: &str) -> Result<()> {
unsafe {
self.set_raw_value::<c_char>(to_c_string!(value));
}
Ok(())
}
fn fmt_fields(&self, f: &mut fmt::DebugStruct) {
f.field("choices", &MaybeListFmt(|| self.choices_iter())).field("choice", &self.choice());
}
}
impl DateWidget {
pub fn timestamp(&self) -> c_int {
unsafe { self.raw_value::<c_int>() }
}
pub fn set_timestamp(&self, value: c_int) {
unsafe { self.set_raw_value::<c_int>(&value) }
}
fn fmt_fields(&self, f: &mut fmt::DebugStruct) {
f.field("timestamp", &self.timestamp());
}
}
impl ButtonWidget {
pub fn press(&self, camera: &Camera) -> Result<()> {
let callback = unsafe { self.raw_value::<libgphoto2_sys::CameraWidgetCallback>() }
.ok_or("Button without callback")?;
Error::check(unsafe { callback(*camera.camera, self.as_ptr(), *camera.context.inner) })?;
Ok(())
}
fn fmt_fields(&self, _f: &mut fmt::DebugStruct) {}
}
impl Widget {
pub(crate) fn new_shared(widget: BackgroundPtr<libgphoto2_sys::CameraWidget>) -> Self {
try_gp_internal!(gp_widget_ref(*widget).unwrap());
Self::new_owned(widget)
}
}