Skip to main content

pushrod/widgets/
image_widget.rs

1// Pushrod Widget Library
2// Image Widget
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::render::callbacks::CallbackRegistry;
17use crate::render::layout_cache::LayoutContainer;
18use crate::render::widget::*;
19use crate::render::widget_cache::WidgetContainer;
20use crate::render::widget_config::{
21    CompassPosition, Config, WidgetConfig, CONFIG_COLOR_BASE, CONFIG_IMAGE_POSITION, CONFIG_SIZE,
22};
23use crate::render::{make_size, Points, Size};
24
25use sdl2::rect::Rect;
26use sdl2::render::{Canvas, Texture, TextureQuery};
27use sdl2::video::Window;
28
29use crate::render::texture_cache::TextureCache;
30use crate::render::texture_store::TextureStore;
31use std::any::Any;
32use std::collections::HashMap;
33
34/// This is the storage object for the `ImageWidget`.  It stores the config, properties, callback registry,
35/// the image name, and a scale flag.
36pub struct ImageWidget {
37    config: WidgetConfig,
38    system_properties: HashMap<i32, String>,
39    callback_registry: CallbackRegistry,
40    texture_store: TextureStore,
41    image_name: String,
42    scaled: bool,
43    texture_sizes: Size,
44}
45
46/// Creates a new `ImageWidget`, which draws an image in a supported image format for SDL2 at a specific
47/// location on the screen.  Requires the name of the image (the full path to the file), the position
48/// within the widget (defined as `ImagePosition`), the xywh bounds, and whether or not the image is
49/// scaled within the bounds of the `Widget`.
50impl ImageWidget {
51    /// Creates a new instance of the `ImageWidget` object.  Requires an image name (full path of the file),
52    /// image position (defined in `ImagePosition`), the `xywh` bounds of the `Widget`, and a scale flag.
53    /// If `scaled` is set to `true`, the image will be scaled within the `Widget` bounds, and the
54    /// `ImagePosition` will be ignored.  Likewise, if set to `false`, the image will be displayed for
55    /// the size of the image, and will be placed in the bounds of the `Widget` based on the position
56    /// specified in the `ImagePosition`.
57    pub fn new(image_name: String, points: Points, size: Size, scaled: bool) -> Self {
58        Self {
59            config: WidgetConfig::new(points, size),
60            system_properties: HashMap::new(),
61            callback_registry: CallbackRegistry::new(),
62            texture_store: TextureStore::default(),
63            image_name,
64            scaled,
65            texture_sizes: make_size(0, 0),
66        }
67    }
68
69    /// Returns the size of the texture.
70    pub fn get_texture_size(&self) -> Size {
71        self.texture_sizes.clone()
72    }
73}
74
75/// This is the `Widget` implementation of the `ImageWidget`.  Image is rendered onto a 3D texture, then
76/// copied to the canvas after rendering.
77impl Widget for ImageWidget {
78    fn draw(&mut self, c: &mut Canvas<Window>, t: &mut TextureCache) -> Option<&Texture> {
79        if self.get_config().invalidated() {
80            let bounds = self.get_config().get_size(CONFIG_SIZE);
81
82            self.texture_store
83                .create_or_resize_texture(c, bounds[0] as u32, bounds[1] as u32);
84
85            let base_color = self.get_color(CONFIG_COLOR_BASE);
86            let image_texture = t.get_image(c, self.image_name.clone());
87            let widget_w = self.get_size(CONFIG_SIZE)[0] as i32;
88            let widget_h = self.get_size(CONFIG_SIZE)[1] as i32;
89            let TextureQuery { width, height, .. } = image_texture.query();
90            let scaled = self.scaled;
91
92            self.texture_sizes = make_size(width, height);
93
94            let texture_x = match self.get_compass(CONFIG_IMAGE_POSITION) {
95                CompassPosition::NW | CompassPosition::W | CompassPosition::SW => 0,
96
97                CompassPosition::N | CompassPosition::Center | CompassPosition::S => {
98                    (widget_w - width as i32) / 2
99                }
100
101                CompassPosition::NE | CompassPosition::E | CompassPosition::SE => {
102                    widget_w - width as i32
103                }
104            };
105
106            let texture_y = match self.get_compass(CONFIG_IMAGE_POSITION) {
107                CompassPosition::NW | CompassPosition::N | CompassPosition::NE => 0,
108
109                CompassPosition::W | CompassPosition::Center | CompassPosition::E => {
110                    (widget_h - height as i32) / 2
111                }
112
113                CompassPosition::SW | CompassPosition::S | CompassPosition::SE => {
114                    widget_h - height as i32
115                }
116            };
117
118            c.with_texture_canvas(self.texture_store.get_mut_ref(), |texture| {
119                texture.set_draw_color(base_color);
120                texture.clear();
121
122                if !scaled {
123                    texture
124                        .copy(
125                            image_texture,
126                            None,
127                            Rect::new(texture_x, texture_y, width, height),
128                        )
129                        .unwrap();
130                } else {
131                    texture
132                        .copy(
133                            image_texture,
134                            None,
135                            Rect::new(0, 0, widget_w as u32, widget_h as u32),
136                        )
137                        .unwrap();
138                }
139            })
140            .unwrap();
141        }
142
143        self.texture_store.get_optional_ref()
144    }
145
146    /// Responds to a screen redraw only if the `CONFIG_IMAGE_POSITION` key was changed.
147    fn on_config_changed(&mut self, _k: u8, _v: Config) {
148        if _k == CONFIG_IMAGE_POSITION {
149            self.get_config().set_invalidated(true);
150        }
151    }
152
153    default_widget_functions!();
154    default_widget_properties!();
155    default_widget_callbacks!();
156}