Continued implementing image widget.
This commit is contained in:
parent
c8d744a18d
commit
b6701a15e3
|
@ -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);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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_::*;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue