1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// Pushrod Widget Library
// Image Widget
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::render::callbacks::CallbackRegistry;
use crate::render::layout_cache::LayoutContainer;
use crate::render::widget::*;
use crate::render::widget_cache::WidgetContainer;
use crate::render::widget_config::{
    CompassPosition, Config, WidgetConfig, CONFIG_COLOR_BASE, CONFIG_IMAGE_POSITION, CONFIG_SIZE,
};
use crate::render::{make_size, Points, Size};

use sdl2::rect::Rect;
use sdl2::render::{Canvas, Texture, TextureQuery};
use sdl2::video::Window;

use crate::render::texture_cache::TextureCache;
use crate::render::texture_store::TextureStore;
use std::any::Any;
use std::collections::HashMap;

/// This is the storage object for the `ImageWidget`.  It stores the config, properties, callback registry,
/// the image name, and a scale flag.
pub struct ImageWidget {
    config: WidgetConfig,
    system_properties: HashMap<i32, String>,
    callback_registry: CallbackRegistry,
    texture_store: TextureStore,
    image_name: String,
    scaled: bool,
    texture_sizes: Size,
}

/// Creates a new `ImageWidget`, which draws an image in a supported image format for SDL2 at a specific
/// location on the screen.  Requires the name of the image (the full path to the file), the position
/// within the widget (defined as `ImagePosition`), the xywh bounds, and whether or not the image is
/// scaled within the bounds of the `Widget`.
impl ImageWidget {
    /// Creates a new instance of the `ImageWidget` object.  Requires an image name (full path of the file),
    /// image position (defined in `ImagePosition`), the `xywh` bounds of the `Widget`, and a scale flag.
    /// If `scaled` is set to `true`, the image will be scaled within the `Widget` bounds, and the
    /// `ImagePosition` will be ignored.  Likewise, if set to `false`, the image will be displayed for
    /// the size of the image, and will be placed in the bounds of the `Widget` based on the position
    /// specified in the `ImagePosition`.
    pub fn new(image_name: String, points: Points, size: Size, scaled: bool) -> Self {
        Self {
            config: WidgetConfig::new(points, size),
            system_properties: HashMap::new(),
            callback_registry: CallbackRegistry::new(),
            texture_store: TextureStore::default(),
            image_name,
            scaled,
            texture_sizes: make_size(0, 0),
        }
    }

    /// Returns the size of the texture.
    pub fn get_texture_size(&self) -> Size {
        self.texture_sizes.clone()
    }
}

/// This is the `Widget` implementation of the `ImageWidget`.  Image is rendered onto a 3D texture, then
/// copied to the canvas after rendering.
impl Widget for ImageWidget {
    fn draw(&mut self, c: &mut Canvas<Window>, t: &mut TextureCache) -> Option<&Texture> {
        if self.get_config().invalidated() {
            let bounds = self.get_config().get_size(CONFIG_SIZE);

            self.texture_store
                .create_or_resize_texture(c, bounds[0] as u32, bounds[1] as u32);

            let base_color = self.get_color(CONFIG_COLOR_BASE);
            let image_texture = t.get_image(c, self.image_name.clone());
            let widget_w = self.get_size(CONFIG_SIZE)[0] as i32;
            let widget_h = self.get_size(CONFIG_SIZE)[1] as i32;
            let TextureQuery { width, height, .. } = image_texture.query();
            let scaled = self.scaled;

            self.texture_sizes = make_size(width, height);

            let texture_x = match self.get_compass(CONFIG_IMAGE_POSITION) {
                CompassPosition::NW | CompassPosition::W | CompassPosition::SW => 0,

                CompassPosition::N | CompassPosition::Center | CompassPosition::S => {
                    (widget_w - width as i32) / 2
                }

                CompassPosition::NE | CompassPosition::E | CompassPosition::SE => {
                    widget_w - width as i32
                }
            };

            let texture_y = match self.get_compass(CONFIG_IMAGE_POSITION) {
                CompassPosition::NW | CompassPosition::N | CompassPosition::NE => 0,

                CompassPosition::W | CompassPosition::Center | CompassPosition::E => {
                    (widget_h - height as i32) / 2
                }

                CompassPosition::SW | CompassPosition::S | CompassPosition::SE => {
                    widget_h - height as i32
                }
            };

            c.with_texture_canvas(self.texture_store.get_mut_ref(), |texture| {
                texture.set_draw_color(base_color);
                texture.clear();

                if !scaled {
                    texture
                        .copy(
                            image_texture,
                            None,
                            Rect::new(texture_x, texture_y, width, height),
                        )
                        .unwrap();
                } else {
                    texture
                        .copy(
                            image_texture,
                            None,
                            Rect::new(0, 0, widget_w as u32, widget_h as u32),
                        )
                        .unwrap();
                }
            })
            .unwrap();
        }

        self.texture_store.get_optional_ref()
    }

    /// Responds to a screen redraw only if the `CONFIG_IMAGE_POSITION` key was changed.
    fn on_config_changed(&mut self, _k: u8, _v: Config) {
        if _k == CONFIG_IMAGE_POSITION {
            self.get_config().set_invalidated(true);
        }
    }

    default_widget_functions!();
    default_widget_properties!();
    default_widget_callbacks!();
}