1use crate::{
2 brush::Brush,
3 core::{algebra::Vector2, color::Color, pool::Handle},
4 define_constructor,
5 draw::{CommandTexture, Draw, DrawingContext, SharedTexture},
6 message::{MessageDirection, UiMessage},
7 widget::{Widget, WidgetBuilder},
8 BuildContext, Control, UiNode, UserInterface,
9};
10use std::{
11 any::{Any, TypeId},
12 ops::{Deref, DerefMut},
13};
14
15#[derive(Debug, Clone, PartialEq)]
16pub enum ImageMessage {
17 Texture(Option<SharedTexture>),
18 Flip(bool),
19}
20
21impl ImageMessage {
22 define_constructor!(ImageMessage:Texture => fn texture(Option<SharedTexture>), layout: false);
23 define_constructor!(ImageMessage:Flip => fn flip(bool), layout: false);
24}
25
26#[derive(Clone)]
27pub struct Image {
28 widget: Widget,
29 texture: Option<SharedTexture>,
30 flip: bool,
31}
32
33crate::define_widget_deref!(Image);
34
35impl Image {
36 pub fn new(widget: Widget) -> Self {
37 Self {
38 widget,
39 texture: None,
40 flip: false,
41 }
42 }
43
44 pub fn set_texture(&mut self, texture: SharedTexture) {
45 self.texture = Some(texture);
46 }
47
48 pub fn texture(&self) -> Option<SharedTexture> {
49 self.texture.clone()
50 }
51}
52
53impl Control for Image {
54 fn query_component(&self, type_id: TypeId) -> Option<&dyn Any> {
55 if type_id == TypeId::of::<Self>() {
56 Some(self)
57 } else {
58 None
59 }
60 }
61
62 fn draw(&self, drawing_context: &mut DrawingContext) {
63 let bounds = self.widget.screen_bounds();
64 let tex_coords = if self.flip {
65 Some([
66 Vector2::new(0.0, 0.0),
67 Vector2::new(1.0, 0.0),
68 Vector2::new(1.0, -1.0),
69 Vector2::new(0.0, -1.0),
70 ])
71 } else {
72 None
73 };
74 drawing_context.push_rect_filled(&bounds, tex_coords.as_ref());
75 let texture = self
76 .texture
77 .as_ref()
78 .map_or(CommandTexture::None, |t| CommandTexture::Texture(t.clone()));
79 drawing_context.commit(self.clip_bounds(), self.widget.background(), texture, None);
80 }
81
82 fn handle_routed_message(&mut self, ui: &mut UserInterface, message: &mut UiMessage) {
83 self.widget.handle_routed_message(ui, message);
84
85 if let Some(msg) = message.data::<ImageMessage>() {
86 if message.destination() == self.handle {
87 match msg {
88 ImageMessage::Texture(tex) => {
89 self.texture = tex.clone();
90 }
91 &ImageMessage::Flip(flip) => {
92 self.flip = flip;
93 }
94 }
95 }
96 }
97 }
98}
99
100pub struct ImageBuilder {
101 widget_builder: WidgetBuilder,
102 texture: Option<SharedTexture>,
103 flip: bool,
104}
105
106impl ImageBuilder {
107 pub fn new(widget_builder: WidgetBuilder) -> Self {
108 Self {
109 widget_builder,
110 texture: None,
111 flip: false,
112 }
113 }
114
115 pub fn with_flip(mut self, flip: bool) -> Self {
116 self.flip = flip;
117 self
118 }
119
120 pub fn with_texture(mut self, texture: SharedTexture) -> Self {
121 self.texture = Some(texture);
122 self
123 }
124
125 pub fn with_opt_texture(mut self, texture: Option<SharedTexture>) -> Self {
126 self.texture = texture;
127 self
128 }
129
130 pub fn build_node(mut self) -> UiNode {
131 if self.widget_builder.background.is_none() {
132 self.widget_builder.background = Some(Brush::Solid(Color::WHITE))
133 }
134
135 let image = Image {
136 widget: self.widget_builder.build(),
137 texture: self.texture,
138 flip: self.flip,
139 };
140 UiNode::new(image)
141 }
142
143 pub fn build(self, ctx: &mut BuildContext) -> Handle<UiNode> {
144 ctx.add_node(self.build_node())
145 }
146}