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
//! A type used to manage a user's image data and map them to `Image` widgets:
//!
//! - [Map](./struct.Map.html)

use std;
use fnv;

/// Unique image identifier.
///
/// Throughout conrod, images are referred to via their unique `Id`. By referring to images via
/// `Id`s, conrod can remain agnostic of the actual image or texture types used to represent each
/// image.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Id(u32);

/// A type used to map the `widget::Id` of `Image` widgets to their associated `Img` data.
///
/// The `image::Map` type is usually instantiated and loaded during the "setup" stage of the
/// application before the main loop begins.
pub struct Map<Img> {
    next_index: u32,
    map: HashMap<Img>,
    /// Whether or not the `image::Map` will trigger a redraw the next time `Ui::draw` is called.
    ///
    /// This is automatically set to `true` when any method that takes `&mut self` is called.
    pub trigger_redraw: std::cell::Cell<bool>,
}

/// The type of `std::collections::HashMap` with `fnv::FnvHasher` used within the `image::Map`.
pub type HashMap<Img> = fnv::FnvHashMap<Id, Img>;

/// An iterator yielding an `Id` for each new `Img` inserted into the `Map` via the `extend`
/// method.
pub struct NewIds {
    index_range: std::ops::Range<u32>,
}


impl<Img> std::ops::Deref for Map<Img> {
    type Target = HashMap<Img>;
    fn deref(&self) -> &Self::Target {
        &self.map
    }
}


impl<Img> Map<Img> {

    /// Construct a new, empty `image::Map`.
    pub fn new() -> Self {
        Map {
            next_index: 0,
            map: HashMap::<Img>::default(),
            trigger_redraw: std::cell::Cell::new(true),
        }
    }

    // Calling any of the following methods will trigger a redraw when using `Ui::draw_if_changed`.

    /// Uniquely borrow the `Img` associated with the given widget.
    ///
    /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called.
    pub fn get_mut(&mut self, id: Id) -> Option<&mut Img> {
        self.trigger_redraw.set(true);
        self.map.get_mut(&id)
    }

    /// Inserts the given image into the map, returning its associated `image::Id`. The user *must*
    /// store the returned `image::Id` in order to use, modify or remove the inserted image.
    ///
    /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called.
    pub fn insert(&mut self, img: Img) -> Id {
        self.trigger_redraw.set(true);
        let index = self.next_index;
        self.next_index = index.wrapping_add(1);
        let id = Id(index);
        self.map.insert(id, img);
        id
    }

    /// Replaces the given image in the map if it exists. Returns the image or None.
    ///
    /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called.
    pub fn replace(&mut self, id: Id, img: Img) -> Option<Img> {
        self.trigger_redraw.set(true);
        self.map.insert(id, img)
    }

    /// Removes the given image from the map if it exists. Returns the image or None.
    ///
    /// Any future use of the given `image::Id` will be invalid.
    ///
    /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called.
    pub fn remove(&mut self, id: Id) -> Option<Img> {
        self.trigger_redraw.set(true);
        self.map.remove(&id)
    }

    /// Insert each of the images yielded by the given iterator and produce an iterator yielding
    /// their generated `Ids` in the same order.
    ///
    /// Note: Calling this will trigger a redraw the next time `Ui::draw_if_changed` is called.
    pub fn extend<I>(&mut self, images: I) -> NewIds
        where I: IntoIterator<Item=Img>,
    {
        self.trigger_redraw.set(true);
        let start_index = self.next_index;
        let mut end_index = start_index;
        for image in images {
            self.map.insert(Id(end_index), image);
            end_index += 1;
        }
        NewIds { index_range: start_index..end_index }
    }

}

impl Iterator for NewIds {
    type Item = Id;
    fn next(&mut self) -> Option<Self::Item> {
        self.index_range.next().map(|i| Id(i))
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        (0, Some(self.len()))
    }
}

impl ExactSizeIterator for NewIds {
    fn len(&self) -> usize {
        self.index_range.len()
    }
}