From a171cb9355dc373a84f13a63ec74743efc863bf9 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Mon, 27 Jul 2026 21:30:18 +0200 Subject: [PATCH] implement imageviewer --- ui-rs/src/ui/ffi.rs | 5 + ui-rs/src/ui/image.rs | 355 ++++++++++++++++++++++++++++++++++++++++ ui-rs/src/ui/label.rs | 28 ++++ ui-rs/src/ui/mod.rs | 2 + ui-rs/src/ui/toolkit.rs | 8 + 5 files changed, 398 insertions(+) create mode 100644 ui-rs/src/ui/image.rs diff --git a/ui-rs/src/ui/ffi.rs b/ui-rs/src/ui/ffi.rs index b448364..2fc9ab6 100644 --- a/ui-rs/src/ui/ffi.rs +++ b/ui-rs/src/ui/ffi.rs @@ -245,4 +245,9 @@ pub struct UiToolbarContentToggleItemArgs { #[repr(C)] pub struct UiToolbarMenuArgs { _private: [u8; 0], +} + +#[repr(C)] +pub struct UiImageViewerArgs { + _private: [u8; 0], } \ No newline at end of file diff --git a/ui-rs/src/ui/image.rs b/ui-rs/src/ui/image.rs new file mode 100644 index 0000000..eae4e3b --- /dev/null +++ b/ui-rs/src/ui/image.rs @@ -0,0 +1,355 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#![allow(dead_code)] + +use std::ffi::{c_char, c_int, c_void, CString}; +use std::path::Path; +use crate::ui::{ffi, toolkit, UiContext, UiImage}; +use crate::ui::ffi::{UiImageViewerArgs, UiLabelArgs}; +use crate::ui::label::{Label, LabelCreate}; +use crate::ui::widget::Widget; + +#[cfg(unix)] +fn path_to_cstring(path: &Path) -> Result { + use std::os::unix::ffi::OsStrExt; + CString::new(path.as_os_str().as_bytes()) +} + +#[cfg(windows)] +fn path_to_cstring(path: &Path) -> Result { + CString::new(path.to_string_lossy().as_bytes()) +} + +impl UiImage { + pub fn init(&mut self, ctx: &UiContext, name: Option<&str>) { + let c_string = name.map(|n| CString::new(n).unwrap()); + let c_str = c_string.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()); + unsafe { + self.ptr = crate::ui::toolkit::ui_generic_new(ctx.ptr, c_str); + } + } + + pub fn load_image_file(&mut self, path: &Path) -> Result<(), i32> { + if self.ptr.is_null() { + return Err(-2); + } + + let path_str = match path_to_cstring(path) { + Ok(s) => s, + Err(_) => return Err(-1), + }; + let ret = unsafe { + ui_image_load_file(self.ptr, path_str.as_ptr()) + }; + + if ret == 0 { + Ok(()) + } else { + Err(ret) + } + } + + pub fn image_load_data(&mut self, data: &[u8]) -> Result<(), i32> { + let ret = unsafe { + ui_image_load_data(self.ptr, data.as_ptr().cast(), data.len()) + }; + + if ret == 0 { + Ok(()) + } else { + Err(ret) + } + } +} + +pub struct ImageViewer { + ptr: *mut c_void +} + +impl Widget for ImageViewer { + fn get_widget(&self) -> *mut c_void { + self.ptr + } +} + +pub struct ImageViewerBuilder<'a, T> { + args: *mut UiImageViewerArgs, + obj: &'a mut toolkit::UiObject, +} + +impl<'a, T> Drop for ImageViewerBuilder<'a, T> { + fn drop(&mut self) { + unsafe { + ui_imageviewer_args_free(self.args); + } + } +} + +impl<'a, T> ImageViewerBuilder<'a, T> { + pub fn create(&mut self) -> ImageViewer { + ImageViewer { ptr: unsafe { ui_imageviewer_create(self.obj.ptr, self.args) } } + } + + pub fn fill(&mut self, fill: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_fill(self.args, fill as c_int); + } + self + } + + pub fn hexpand(&mut self, value: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_hexpand(self.args, value as c_int); + } + self + } + + pub fn vexpand(&mut self, value: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_vexpand(self.args, value as c_int); + } + self + } + + pub fn hfill(&mut self, value: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_hfill(self.args, value as c_int); + } + self + } + + pub fn vfill(&mut self, value: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_vfill(self.args, value as c_int); + } + self + } + + pub fn override_defaults(&mut self, value: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_override_defaults(self.args, value as c_int); + } + self + } + + pub fn margin(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_margin(self.args, value); + } + self + } + + pub fn margin_left(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_margin_left(self.args, value); + } + self + } + + pub fn margin_right(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_margin_right(self.args, value); + } + self + } + + pub fn margin_top(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_margin_top(self.args, value); + } + self + } + + pub fn margin_bottom(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_margin_bottom(self.args, value); + } + self + } + + pub fn colspan(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_colspan(self.args, value); + } + self + } + + pub fn rowspan(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_rowspan(self.args, value); + } + self + } + + pub fn name(&mut self, value: &str) -> &mut Self { + let cstr = CString::new(value).unwrap(); + unsafe { + ui_imageviewer_args_set_name(self.args, cstr.as_ptr()); + } + self + } + + pub fn style_class(&mut self, value: &str) -> &mut Self { + let cstr = CString::new(value).unwrap(); + unsafe { + ui_imageviewer_args_set_style_class(self.args, cstr.as_ptr()); + } + self + } + + pub fn value(&mut self, value: &toolkit::UiImage) -> &mut Self{ + unsafe { + ui_imageviewer_args_set_value(self.args, value.ptr); + } + self + } + + pub fn varname(&mut self, varname: &str) -> &mut Self { + let cstr = CString::new(varname).unwrap(); + unsafe { + ui_imageviewer_args_set_varname(self.args, cstr.as_ptr()); + } + self + } + + pub fn scrollview(&mut self, enable: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_scrollarea(self.args, enable as c_int); + } + self + } + + pub fn autoscale(&mut self, enable: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_autoscale(self.args, enable as c_int); + } + self + } + + pub fn adjustwidgetsize(&mut self, enable: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_adjustwidgetsize(self.args, enable as c_int); + } + self + } + + pub fn useradjustable(&mut self, enable: bool) -> &mut Self { + unsafe { + ui_imageviewer_args_set_useradjustable(self.args, enable as c_int); + } + self + } + + pub fn padding(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_image_padding(self.args, value as c_int); + } + self + } + + pub fn padding_left(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_image_padding_left(self.args, value as c_int); + } + self + } + + pub fn padding_right(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_image_padding_right(self.args, value as c_int); + } + self + } + + pub fn padding_top(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_image_padding_top(self.args, value as c_int); + } + self + } + + pub fn padding_bottom(&mut self, value: i32) -> &mut Self { + unsafe { + ui_imageviewer_args_set_image_padding_bottom(self.args, value as c_int); + } + self + } + + pub fn visibility_states(&mut self, states: &[i32]) -> &mut Self { + unsafe { + ui_imageviewer_args_set_visibility_states(self.args, states.as_ptr(), states.len() as c_int); + } + self + } +} + +/* -------------------------------- C functions -------------------------------- */ + +unsafe extern "C" { + fn ui_image_load_file(obj: *mut ffi::UiGeneric, path: *const c_char) -> c_int; + fn ui_image_load_data(obj: *mut ffi::UiGeneric, imgdata: *const c_void, size: libc::size_t) -> c_int; + + fn ui_imageviewer_create(obj: *mut ffi::UiObject, args: *const ffi::UiImageViewerArgs) -> *mut c_void; + + fn ui_imageviewer_args_new() -> *mut UiImageViewerArgs; + fn ui_imageviewer_args_set_fill(args: *mut UiImageViewerArgs, fill: c_int); + fn ui_imageviewer_args_set_hexpand(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_vexpand(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_hfill(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_vfill(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_override_defaults(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_margin(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_margin_left(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_margin_right(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_margin_top(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_margin_bottom(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_colspan(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_rowspan(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_name(args: *mut UiImageViewerArgs, name: *const c_char); + fn ui_imageviewer_args_set_style_class(args: *mut UiImageViewerArgs, classname: *const c_char); + fn ui_imageviewer_args_set_style(args: *mut UiImageViewerArgs, style: c_int); + fn ui_imageviewer_args_set_value(args: *mut UiImageViewerArgs, value: *mut ffi::UiGeneric); + fn ui_imageviewer_args_set_varname(args: *mut UiImageViewerArgs, varname: *const c_char); + fn ui_imageviewer_args_set_scrollarea(args: *mut UiImageViewerArgs, fill: c_int); + fn ui_imageviewer_args_set_autoscale(args: *mut UiImageViewerArgs, fill: c_int); + fn ui_imageviewer_args_set_adjustwidgetsize(args: *mut UiImageViewerArgs, fill: c_int); + fn ui_imageviewer_args_set_useradjustable(args: *mut UiImageViewerArgs, fill: c_int); + fn ui_imageviewer_args_set_image_padding(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_image_padding_left(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_image_padding_right(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_image_padding_top(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_image_padding_bottom(args: *mut UiImageViewerArgs, value: c_int); + fn ui_imageviewer_args_set_visibility_states(args: *mut UiImageViewerArgs, states: *const c_int, numstates: c_int); + fn ui_imageviewer_args_free(args: *mut UiImageViewerArgs); + + fn ui_imageviewer_reset(widget: *mut c_void); + fn ui_imageviewer_set_autoscale(widget: *mut c_void, set: c_int); + fn ui_imageviewer_set_adjustwidgetsize(widget: *mut c_void, set: c_int); + fn ui_imageviewer_set_useradjustable(widget: *mut c_void, set: c_int); +} \ No newline at end of file diff --git a/ui-rs/src/ui/label.rs b/ui-rs/src/ui/label.rs index 094345c..14de16a 100644 --- a/ui-rs/src/ui/label.rs +++ b/ui-rs/src/ui/label.rs @@ -1,3 +1,31 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2026 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + use std::ffi::{c_char, c_int, c_void, CString}; use crate::ui::{ffi, toolkit, Button, ButtonBuilder}; use crate::ui::ffi::{UiButtonArgs, UiLabelArgs, UiObject, UiString, UiToggleArgs}; diff --git a/ui-rs/src/ui/mod.rs b/ui-rs/src/ui/mod.rs index dd968c4..338d463 100644 --- a/ui-rs/src/ui/mod.rs +++ b/ui-rs/src/ui/mod.rs @@ -40,6 +40,7 @@ mod menu; mod toolbar; mod label; mod icon; +mod image; pub use toolkit::*; pub use event::*; @@ -52,3 +53,4 @@ pub use list::*; pub use toolbar::*; pub use menu::*; pub use icon::*; +pub use image::*; diff --git a/ui-rs/src/ui/toolkit.rs b/ui-rs/src/ui/toolkit.rs index 001dd9b..3e494db 100644 --- a/ui-rs/src/ui/toolkit.rs +++ b/ui-rs/src/ui/toolkit.rs @@ -34,6 +34,7 @@ use crate::ui::{action_event_wrapper, event, ffi, simple_event_wrapper, ui_objec use std::marker::PhantomData; use std::mem; +use std::path::Path; use std::ptr::null_mut; use std::slice::{Iter, IterMut}; use std::sync::{Arc, Mutex}; @@ -727,6 +728,10 @@ pub struct UiSourceList { sublists: Vec> } +pub struct UiImage { + pub ptr: *mut ffi::UiGeneric +} + /* -------------------------------- Default implementation -------------------------------- */ macro_rules! value_default_impl { @@ -752,6 +757,7 @@ macro_rules! value_default_impl2 { value_default_impl!(UiInteger); value_default_impl!(UiDouble); value_default_impl!(UiString); +value_default_impl!(UiImage); value_default_impl2!(UiText); impl Default for UiList { @@ -1469,6 +1475,8 @@ unsafe extern "C" { fn ui_string_set(value: *const ffi::UiString, str: *const c_char); fn ui_int_get(value: *const ffi::UiInteger) -> i64; fn ui_int_set(value: *const ffi::UiInteger, i: i64); + + pub fn ui_generic_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiGeneric; } type UiListInitFunc = extern "C" fn(*mut ffi::UiContext, *mut ffi::UiList, *mut c_void); -- 2.52.0