cog_task/action/core/
image.rs1use crate::action::{Action, Props, StatefulAction, INFINITE, VISUAL};
2use crate::comm::QWriter;
3use crate::resource::{Color, IoManager, ResourceAddr, ResourceManager, ResourceValue};
4use crate::server::{AsyncSignal, Config, State, SyncSignal};
5use eframe::egui;
6use eframe::egui::{CentralPanel, Color32, CursorIcon, Frame, TextureId, Vec2};
7use eyre::{eyre, Result};
8use serde::{Deserialize, Serialize};
9use std::path::PathBuf;
10
11#[derive(Debug, Deserialize, Serialize)]
12#[serde(deny_unknown_fields)]
13pub struct Image {
14 src: PathBuf,
15 #[serde(default)]
16 width: Option<f32>,
17 #[serde(default)]
18 background: Color,
19}
20
21stateful!(Image {
22 handle: TextureId,
23 size: Vec2,
24 width: Option<f32>,
25 background: Color32,
26});
27
28impl Image {
29 #[inline(always)]
30 pub fn new(src: PathBuf, width: Option<f32>, background: Color) -> Self {
31 Self {
32 src,
33 width,
34 background,
35 }
36 }
37}
38
39impl Action for Image {
40 #[inline(always)]
41 fn resources(&self, _config: &Config) -> Vec<ResourceAddr> {
42 vec![ResourceAddr::Image(self.src.to_owned())]
43 }
44
45 fn stateful(
46 &self,
47 _io: &IoManager,
48 res: &ResourceManager,
49 _config: &Config,
50 _sync_writer: &QWriter<SyncSignal>,
51 _async_writer: &QWriter<AsyncSignal>,
52 ) -> Result<Box<dyn StatefulAction>> {
53 let src = ResourceAddr::Image(self.src.clone());
54 let (texture, size) = {
55 if let ResourceValue::Image(texture, size) = res.fetch(&src)? {
56 (texture, size)
57 } else {
58 return Err(eyre!("Resource value and address types don't match."));
59 }
60 };
61
62 Ok(Box::new(StatefulImage {
63 done: false,
64 handle: texture,
65 size,
66 width: self.width,
67 background: self.background.into(),
68 }))
69 }
70}
71
72impl StatefulAction for StatefulImage {
73 impl_stateful!();
74
75 #[inline]
76 fn props(&self) -> Props {
77 (INFINITE | VISUAL).into()
78 }
79
80 fn show(
81 &mut self,
82 ui: &mut egui::Ui,
83 _sync_writer: &mut QWriter<SyncSignal>,
84 _async_writer: &mut QWriter<AsyncSignal>,
85 _state: &State,
86 ) -> Result<()> {
87 ui.output().cursor_icon = CursorIcon::None;
88
89 CentralPanel::default()
90 .frame(Frame::default().fill(self.background))
91 .show_inside(ui, |ui| {
92 ui.centered_and_justified(|ui| {
93 if let Some(width) = self.width {
94 let scale = width / self.size.x;
95 ui.image(self.handle, self.size * scale);
96 } else {
97 ui.image(self.handle, self.size);
98 }
99 });
100 });
101
102 Ok(())
103 }
104
105 fn debug(&self) -> Vec<(&str, String)> {
106 <dyn StatefulAction>::debug(self)
107 .into_iter()
108 .chain([
109 ("texture_id", format!("{:?}", self.handle)),
110 ("size", format!("{:?}", self.size)),
111 ])
112 .collect()
113 }
114}