use std::sync::LazyLock;
use mat::Mat;
use crate::prelude::*;
pub use self::builder::{PictureBuilder as Builder, FitBuilder, WithFrameBuilder};
pub type Picture <'element> = Widget <'element, Scroll, Pixmap, Image>;
pub static CONTROLS : LazyLock <Controls> = LazyLock::new (Controls::default);
pub fn scroll_right (
_ : &controls::button::Release,
_elements : &Tree <Element>,
_node_id : &NodeId,
_action_buffer : &mut Vec <(NodeId, Action)>
) {
log::trace!("scroll_right...");
unimplemented!("TODO: picture scroll right")
}
pub(crate) fn image (
canvas : &Canvas,
scroll : &Scroll,
pixmap : &Pixmap
) -> Image {
use model::component::Pixmap;
match &pixmap {
Pixmap::Pixmap8 (mat) => Image::Raw8 (make_image (canvas, scroll, mat)),
Pixmap::Pixmap16 (mat) => Image::Raw16 (make_image (canvas, scroll, mat)),
Pixmap::Pixmap32 (mat) => Image::Raw32 (make_image (canvas, scroll, mat)),
Pixmap::Pixmap64 (mat) => Image::Raw64 (make_image (canvas, scroll, mat)),
Pixmap::Texture { resource_id, index, .. } =>
Image::Texture (Texture {
resource: *resource_id,
index: *index
})
}
}
fn make_image <T : Copy> (
canvas : &Canvas,
_scroll : &Scroll,
pixmap : &Mat <T>
) -> Mat <T> {
let (image_width, image_height) = canvas.body_wh();
let columns = pixmap.width().min (image_width as usize);
let rows = pixmap.height().min (image_height as usize);
let v = if rows > 0 {
pixmap.rows().take (rows).flat_map (|row| &row[0..columns]).copied()
.collect::<Vec <T>>()
} else {
Vec::new()
};
Mat::from_vec ((rows, columns).into(), v).unwrap()
}
mod builder {
use derive_builder::Builder;
use crate::prelude::*;
#[derive(Builder)]
#[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
struct Picture <'a, A : Application> {
elements : &'a Tree <Element>,
parent_id : &'a NodeId,
#[builder(default)]
bindings : Option <&'a Bindings <A>>,
#[builder(default)]
frame_appearances : Appearances,
#[builder(default)]
frame_border : Option <Border>,
#[builder(default)]
frame_clear_color : Option <canvas::ClearColor>,
#[builder(default)]
frame_layout : Layout,
#[builder(default)]
name : Option <String>,
#[builder(default)]
pixmap : Pixmap
}
#[derive(Builder)]
#[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
struct Fit <'a, A : Application> {
elements : &'a Tree <Element>,
parent_id : &'a NodeId,
#[builder(default)]
anchor : Alignment,
#[builder(default)]
appearances : Appearances,
#[builder(default)]
bindings : Option <&'a Bindings <A>>,
#[builder(default)]
border : Option <Border>,
#[builder(default)]
clear_color : Option <canvas::ClearColor>,
#[builder(default)]
coord_kind_override : Option <coordinates::Kind>,
#[builder(default)]
name : Option <String>,
#[builder(default)]
offset : Offset,
#[builder(default)]
pixmap : Pixmap
}
#[derive(Builder)]
#[builder(public, pattern="owned", build_fn(private), setter(strip_option))]
struct WithFrame <'a, A : Application> {
elements : &'a Tree <Element>,
frame_id : &'a NodeId,
#[builder(default)]
bindings : Option <&'a Bindings <A>>,
#[builder(default)]
name : Option <String>,
#[builder(default)]
pixmap : Pixmap,
}
impl <'a, A : Application> PictureBuilder <'a, A> {
pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
PictureBuilder {
elements: Some (elements),
parent_id: Some (parent_id),
bindings: None,
frame_appearances: None,
frame_border: None,
frame_clear_color: None,
frame_layout: None,
name: None,
pixmap: None
}
}
}
impl <A : Application> BuildActions for PictureBuilder <'_, A> {
fn build_actions (self) -> Vec<(NodeId, Action)> {
use std::convert::TryInto;
use crate::tree::InsertBehavior;
log::trace!("build actions...");
let Picture {
elements, parent_id, bindings, frame_appearances, frame_border,
frame_clear_color, frame_layout, name, pixmap
} = self.build()
.map_err(|err| log::error!("frame builder error: {err:?}"))
.unwrap();
let bindings_empty = Bindings::empty();
let bindings = bindings.unwrap_or (&bindings_empty);
{ let Widget (_, _, canvas) = Frame::try_get (elements, parent_id).unwrap();
assert!(canvas.coordinates.kind() == coordinates::Kind::Tile);
}
let mut out = vec![];
let (mut subtree, order) = {
let mut actions = {
let mut frame = frame::Builder::new (elements, parent_id)
.appearances (frame_appearances)
.bindings (bindings)
.layout (frame_layout);
set_option!(frame, border, frame_border);
set_option!(frame, clear_color, frame_clear_color);
frame.build_actions()
};
out.extend (actions.drain (1..));
debug_assert_eq!(actions.len(), 1);
actions.pop().unwrap().1.try_into().unwrap()
};
let frame_id = subtree.root_node_id().unwrap().clone();
let picture = {
let Widget (_, _, canvas) = Frame::try_get (&subtree, &frame_id).unwrap();
let scroll = Scroll::default_pixel_absolute();
let image = super::image (canvas, &scroll, &pixmap);
let controller = {
let mut controller = Controller::with_bindings (
&bindings.get_bindings (&super::CONTROLS));
controller.component = scroll.into();
controller
};
let model = Model { component: pixmap.into(), .. Model::default() };
let view = view::Component::from (image).into();
let name = name.unwrap_or ("Picture".to_string());
Element::new (name, controller, model, view)
};
let _ = subtree
.insert (Node::new (picture), InsertBehavior::UnderNode (&frame_id))
.unwrap();
out.push ((parent_id.clone(), Action::Create (subtree, order)));
log::trace!("...build actions");
out
}
}
impl <'a, A : Application> FitBuilder <'a, A> {
pub const fn new (elements : &'a Tree <Element>, parent_id : &'a NodeId) -> Self {
FitBuilder {
elements: Some (elements),
parent_id: Some (parent_id),
anchor: None,
appearances: None,
bindings: None,
border: None,
clear_color: None,
coord_kind_override: None,
name: None,
offset: None,
pixmap: None
}
}
}
impl <A : Application> BuildActions for FitBuilder <'_, A> {
fn build_actions (self) -> Vec<(NodeId, Action)> {
use crate::tree::InsertBehavior;
log::trace!("fit build actions...");
let Fit {
elements, parent_id, anchor, appearances, bindings, border, clear_color,
coord_kind_override, name, offset, pixmap
} = self.build().map_err (|err| log::error!("frame builder error: {err:?}"))
.unwrap();
let bindings_empty = Bindings::empty();
let bindings = bindings.unwrap_or (&bindings_empty);
let (body_width, body_height) = {
let Widget (_, _, canvas) = Frame::try_get (elements, parent_id).unwrap();
if let Some (coord_kind) = coord_kind_override {
if canvas.coordinates.kind() == coord_kind {
(pixmap.width() as u32, pixmap.height() as u32)
} else {
match canvas.coordinates.kind() {
coordinates::Kind::Tile => {
let round_up = |dimension, tile|
dimension as u32 / tile + u32::from (dimension as u32 % tile != 0);
let [tile_w, tile_h] = *coordinates::TILE_WH;
( round_up (pixmap.width(), tile_w),
round_up (pixmap.height(), tile_h)
)
}
coordinates::Kind::Pixel =>
unimplemented!("TODO: pixel frame with tile image")
}
}
} else {
(pixmap.width() as u32, pixmap.height() as u32)
}
};
let mut out = vec![];
let frame = {
let size = {
let (border_w, border_h) = border.as_ref().map_or ((0, 0), Border::total_wh);
let width = (body_width + border_w as u32).into();
let height = (body_height + border_h as u32).into();
Size { width, height }
};
let layout = layout::Free { anchor, offset, size };
let mut free = frame::free::Builder::new (elements, parent_id)
.appearances (appearances)
.bindings (bindings)
.layout (layout);
set_option!(free, border, border);
set_option!(free, clear_color, clear_color);
free.build_element()
};
let mut subtree = TreeBuilder::new().with_root (Node::new (frame)).build();
let frame_id = subtree.root_node_id().unwrap().clone();
let picture = {
let Widget (_, _, canvas) = Frame::try_get (&subtree, &frame_id).unwrap();
let scroll = Scroll::default_pixel_absolute();
let image = super::image (canvas, &scroll, &pixmap);
let controller = {
let mut controller =
Controller::with_bindings (&bindings.get_bindings (&super::CONTROLS));
controller.component = scroll.into();
controller
};
let model = Model { component: pixmap.into(), .. Model::default() };
let view = view::Component::from (image).into();
let name = name.unwrap_or ("Picture".to_string());
Element::new (name, controller, model, view)
};
let _ = subtree
.insert (Node::new (picture), InsertBehavior::UnderNode (&frame_id))
.unwrap();
out.push ((parent_id.clone(), Action::Create (subtree, CreateOrder::Append)));
log::trace!("...fit build actions");
out
}
}
impl <'a, A : Application> WithFrameBuilder <'a, A> {
pub const fn new (elements : &'a Tree <Element>, frame_id : &'a NodeId) -> Self {
WithFrameBuilder {
elements: Some (elements),
frame_id: Some (frame_id),
bindings: None,
name: None,
pixmap: None
}
}
}
impl <A : Application> BuildElement for WithFrameBuilder <'_, A> {
fn build_element (self) -> Element {
log::trace!("with frame build actions...");
let WithFrame { elements, frame_id, bindings, name, pixmap } = self.build()
.map_err (|err| log::error!("with frame builder error: {err:?}"))
.unwrap();
let bindings_empty = Bindings::empty();
let bindings = bindings.unwrap_or (&bindings_empty)
.get_bindings (&super::CONTROLS);
let Widget (_, _, canvas) = Frame::try_get (elements, frame_id).unwrap();
assert!(canvas.coordinates.kind() == coordinates::Kind::Tile);
let scroll = Scroll::default_pixel_absolute();
let image = super::image (canvas, &scroll, &pixmap);
let controller = {
let mut controller =
Controller::with_bindings (&bindings.get_bindings (&super::CONTROLS));
controller.component = scroll.into();
controller
};
let model = Model { component: pixmap.into(), .. Model::default() };
let view = view::Component::from (image).into();
let name = name.unwrap_or ("Picture".to_string());
log::trace!("...with_frame");
Element::new (name, controller, model, view)
}
}
}