use std::mem;
use zng_app::render::RepeatMode;
use zng_ext_image::{ImageCacheMode, ImageRenderArgs, ImageSource, Img, IMAGES};
use zng_wgt::prelude::*;
use crate::{IMAGE_CACHE_VAR, IMAGE_LIMITS_VAR, IMAGE_RENDERING_VAR};
#[property(BORDER)]
pub fn border_img(
child: impl UiNode,
widths: impl IntoVar<SideOffsets>,
source: impl IntoVar<ImageSource>,
slices: impl IntoVar<SideOffsets>,
) -> impl UiNode {
let widths = widths.into_var();
let source = source.into_var();
let slices = slices.into_var();
let mut img = var(Img::dummy(None)).read_only();
let mut _img_sub = VarHandle::dummy();
let mut slices_img_size = PxSize::zero();
let mut slices_px = PxSideOffsets::zero();
border_node(
child,
widths,
match_node_leaf(move |op| match op {
UiNodeOp::Init => {
WIDGET
.sub_var(&source)
.sub_var(&IMAGE_CACHE_VAR)
.sub_var_render(&slices)
.sub_var_render(&BORDER_IMG_REPEAT_VAR)
.sub_var_render(&IMAGE_RENDERING_VAR)
.sub_var_render(&BORDER_IMG_FILL_VAR);
let mode = if IMAGE_CACHE_VAR.get() {
ImageCacheMode::Cache
} else {
ImageCacheMode::Ignore
};
let limits = IMAGE_LIMITS_VAR.get();
let mut source = source.get();
if let ImageSource::Render(_, args) = &mut source {
*args = Some(ImageRenderArgs { parent: Some(WINDOW.id()) });
}
img = IMAGES.image(source, mode, limits, None, None);
_img_sub = img.subscribe(UpdateOp::Update, WIDGET.id());
let img = img.get();
if img.is_loaded() {
slices_img_size = img.size();
}
}
UiNodeOp::Deinit => {
img = var(Img::dummy(None)).read_only();
_img_sub = VarHandle::dummy();
}
UiNodeOp::Update { .. } => {
if source.is_new() {
let mut source = source.get();
if let ImageSource::Render(_, args) = &mut source {
*args = Some(ImageRenderArgs { parent: Some(WINDOW.id()) });
}
let mode = if IMAGE_CACHE_VAR.get() {
ImageCacheMode::Cache
} else {
ImageCacheMode::Ignore
};
let limits = IMAGE_LIMITS_VAR.get();
img = IMAGES.image(source, mode, limits, None, None);
} else if let Some(enabled) = IMAGE_CACHE_VAR.get_new() {
let is_cached = img.with(|img| IMAGES.is_cached(img));
if enabled != is_cached {
img = if is_cached {
let img = mem::replace(&mut img, var(Img::dummy(None)).read_only());
IMAGES.detach(img)
} else {
let source = source.get();
let limits = IMAGE_LIMITS_VAR.get();
IMAGES.image(source, ImageCacheMode::Cache, limits, None, None)
};
}
}
if img.is_new() {
let img = img.get();
if img.is_loaded() {
let s = img.size();
if s != slices_img_size {
slices_img_size = s;
WIDGET.layout();
}
}
WIDGET.render();
}
}
UiNodeOp::Measure { desired_size, .. } => {
*desired_size = LAYOUT.constraints().fill_size();
}
UiNodeOp::Layout { final_size, .. } => {
*final_size = LAYOUT.constraints().fill_size();
let metrics = LAYOUT
.metrics()
.with_constraints(PxConstraints2d::new_exact_size(slices_img_size))
.with_scale_factor(1.fct());
let s = LAYOUT.with_context(metrics, || slices.layout());
if s != slices_px {
slices_px = s;
WIDGET.render();
}
}
UiNodeOp::Render { frame } => {
let img = img.get();
if img.is_loaded() {
let (rect, offsets) = BORDER.border_layout();
if !rect.size.is_empty() {
let repeats = BORDER_IMG_REPEAT_VAR.get();
frame.push_border_image(
rect,
offsets,
slices_px,
BORDER_IMG_FILL_VAR.get(),
repeats.top_bottom,
repeats.left_right,
&img,
IMAGE_RENDERING_VAR.get(),
);
}
}
}
_ => {}
}),
)
}
#[property(CONTEXT, default(BORDER_IMG_REPEAT_VAR))]
pub fn border_img_repeat(child: impl UiNode, repeats: impl IntoVar<BorderRepeats>) -> impl UiNode {
with_context_var(child, BORDER_IMG_REPEAT_VAR, repeats)
}
#[property(CONTEXT, default(BORDER_IMG_FILL_VAR))]
pub fn border_img_fill(child: impl UiNode, fill: impl IntoVar<bool>) -> impl UiNode {
with_context_var(child, BORDER_IMG_FILL_VAR, fill)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
pub struct BorderRepeats {
pub top_bottom: RepeatMode,
pub left_right: RepeatMode,
}
impl BorderRepeats {
pub fn new<TB: Into<RepeatMode>, LR: Into<RepeatMode>>(top_bottom: TB, left_right: LR) -> Self {
BorderRepeats {
top_bottom: top_bottom.into(),
left_right: left_right.into(),
}
}
pub fn new_all<T: Into<RepeatMode>>(all_sides: T) -> Self {
let all_sides = all_sides.into();
Self::new(all_sides, all_sides)
}
}
context_var! {
pub static BORDER_IMG_REPEAT_VAR: BorderRepeats = BorderRepeats::default();
pub static BORDER_IMG_FILL_VAR: bool = false;
}
impl_from_and_into_var! {
fn from(repeat: RepeatMode) -> BorderRepeats {
BorderRepeats::new_all(repeat)
}
fn from(repeat_or_stretch: bool) -> BorderRepeats {
BorderRepeats::new_all(repeat_or_stretch)
}
fn from<TB: Into<RepeatMode>, LR: Into<RepeatMode>>((top_bottom, left_right): (TB, LR)) -> BorderRepeats {
BorderRepeats::new(top_bottom, left_right)
}
}