Continued implementing image widget.

This commit is contained in:
Well 2021-07-27 01:17:31 -03:00
parent c8d744a18d
commit b6701a15e3
7 changed files with 125 additions and 17 deletions

View File

@ -10,7 +10,8 @@ fn main() {
items = widgets![
example(),
example(),
disabled()
disabled(),
image_button()
];
};
}
@ -41,3 +42,13 @@ fn disabled() -> impl Widget {
content = text("Disabled");
}
}
fn image_button() -> impl Widget {
button! {
on_click = hn!(|_, _| println!("What does this do?"));
content = image!{
path = "examples/res/icon-bytes.png";
size = (32, 32);
};
}
}

View File

@ -1,6 +1,6 @@
//! Image cache API.
use std::{error::Error, path::PathBuf, sync::Arc};
use std::{cell::RefCell, error::Error, path::PathBuf, rc::Rc, sync::Arc};
use crate::{
app::AppExtension,
@ -44,9 +44,12 @@ impl AppExtension for ImageManager {
/// A loaded or loading image.
///
/// This struct is an [`Arc`] pointer, cloning it is cheap.
#[derive(Clone, Debug)]
pub struct Image(Arc<Mutex<ImageState>>);
/// This struct is just pointers, cloning it is cheap.
#[derive(Clone)]
pub struct Image {
state: Arc<Mutex<ImageState>>,
render_keys: Rc<RefCell<Vec<RenderImage>>>,
}
impl Image {
/// Start loading an image file.
pub fn from_file(file: impl Into<PathBuf>) -> Self {
@ -58,12 +61,15 @@ impl Image {
Err(e) => ImageState::Error(Box::new(e)),
}
}));
Image(state)
Image {
state,
render_keys: Rc::default(),
}
}
/// Start loading an image from the web using a GET request.
pub fn from_uri(uri: impl TryUri) -> Self {
match uri.try_into() {
let state = match uri.try_into() {
Ok(uri) => {
let state = Arc::new(Mutex::new(ImageState::Loading));
task::spawn(async_clone_move!(state, {
@ -72,16 +78,23 @@ impl Image {
Err(e) => ImageState::Error(e),
}
}));
Image(state)
state
}
Err(e) => Image(Arc::new(Mutex::new(ImageState::Error(Box::new(e))))),
Err(e) => Arc::new(Mutex::new(ImageState::Error(Box::new(e)))),
};
Image {
state,
render_keys: Rc::default(),
}
}
/// Create a loaded image.
pub fn from_decoded(image: DynamicImage) -> Self {
let state = Arc::new(Mutex::new(Self::decoded_to_state(image)));
Image(state)
Image {
state,
render_keys: Rc::default(),
}
}
fn decoded_to_state(image: DynamicImage) -> ImageState {
@ -146,7 +159,41 @@ impl Image {
}
fn render_image(&self, api: &Arc<RenderApi>) -> ImageKey {
api.generate_image_key()
let namespace = api.get_namespace_id();
let mut keys = self.render_keys.borrow_mut();
for r in keys.iter_mut() {
if r.key.0 == namespace {
if !r.loaded {
if let ImageState::Loaded { descriptor, data } = &*self.state.lock() {
r.loaded = true;
Self::load_image(api, r.key, *descriptor, data.clone())
}
}
return r.key;
}
}
let key = api.generate_image_key();
let mut loaded = false;
if let ImageState::Loaded { descriptor, data } = &*self.state.lock() {
loaded = true;
Self::load_image(api, key, *descriptor, data.clone())
}
keys.push(RenderImage {
api: Arc::downgrade(api),
key,
loaded,
});
key
}
fn load_image(api: &Arc<RenderApi>, key: ImageKey, descriptor: ImageDescriptor, data: ImageData) {
let mut txn = webrender::api::Transaction::new();
txn.add_image(key, descriptor, data, None);
api.update_resources(txn.resource_updates);
}
}
impl crate::render::Image for Image {
@ -154,9 +201,22 @@ impl crate::render::Image for Image {
self.render_image(api)
}
}
#[derive(Debug)]
enum ImageState {
Loading,
Loaded { data: ImageData, descriptor: ImageDescriptor },
Loaded { descriptor: ImageDescriptor, data: ImageData },
Error(Box<dyn Error + Send + Sync>),
}
struct RenderImage {
api: std::sync::Weak<RenderApi>,
key: ImageKey,
loaded: bool,
}
impl Drop for RenderImage {
fn drop(&mut self) {
if let Some(api) = self.api.upgrade() {
let mut txn = webrender::api::Transaction::new();
txn.delete_image(self.key);
api.update_resources(txn.resource_updates);
}
}
}

View File

@ -706,8 +706,9 @@ impl FrameBuilder {
if let Some(api) = &self.api {
let image_key = image.image_key(api);
// self.display_list.push_image(common, bounds, image_rendering, alpha_type, key, color)
todo!();
let rect = LayoutRect::from_size(LayoutSize::new(32.0,32.0));
self.display_list.push_image(&self.common_item_properties(rect), rect, ImageRendering::Auto, AlphaType::Alpha, image_key, RenderColor::TRANSPARENT)
}
}

View File

@ -881,6 +881,9 @@ impl_from_and_into_var! {
fn from(t: Text) -> Cow<'static, str> {
t.0
}
fn from(t: Text) -> std::path::PathBuf {
t.into_owned().into()
}
}
impl From<Text> for Box<dyn std::error::Error> {
fn from(err: Text) -> Self {

View File

@ -960,6 +960,8 @@ pub mod prelude {
#[doc(no_inline)]
pub use crate::core::render::*;
#[doc(no_inline)]
pub use crate::core::image::{Image, ImagesExt};
#[doc(no_inline)]
pub use crate::core::task::{self, rayon::prelude::*, AppTask, WidgetTask};
#[doc(no_inline)]
pub use crate::core::text::*;

View File

@ -6,20 +6,21 @@ pub mod text;
mod button_;
mod container_;
mod window_;
mod fill_color;
mod gradient;
mod image_;
mod line_;
mod slot_;
mod switch_;
mod ui_n;
mod view_;
mod window_;
pub use button_::*;
pub use container_::*;
pub use fill_color::*;
pub use gradient::*;
pub use image_::*;
pub use line_::*;
pub use slot_::*;
pub use switch_::*;

30
zero-ui/widgets/image_.rs Normal file
View File

@ -0,0 +1,30 @@
use crate::prelude::new_widget::*;
#[widget($crate::widgets::image)]
pub mod image {
use super::*;
properties!{
path(impl IntoVar<Text>) = "";
}
fn new_child(path: impl IntoVar<Text>) -> impl UiNode {
struct ImageNode<T> {
path: T,
image: Option<Image>,
}
#[impl_ui_node(none)]
impl<T: Var<Text>> UiNode for ImageNode<T> {
fn init(&mut self, ctx: &mut WidgetContext) {
self.image = Some(Image::from_file(self.path.get_clone(ctx)))
}
fn render(&self, _: &mut RenderContext, frame: &mut FrameBuilder) {
frame.push_image(self.image.as_ref().unwrap())
}
}
ImageNode {
path: path.into_var(),
image: None
}
}
}