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
use super::ScaleMode;
use amethyst_core::specs::prelude::{Component, DenseVecStorage, FlaggedStorage};
use std::marker::PhantomData;

/// The UiTransform represents the transformation of a ui element.
/// Values are in pixel and the position is calculated from the top left of the screen
/// to the center of the ui element's area.
#[derive(Clone, Debug)]
pub struct UiTransform {
    /// An identifier. Serves no purpose other than to help you distinguish between UI elements.
    pub id: String,
    /// X coordinate, 0 is the left edge of the screen, while the width of the screen in pixel is the right edge.
    /// Centered in the middle of the ui element.
    pub local_x: f32,
    /// Y coordinate, 0 is the top edge of the screen, while the height of the screen in pixel is the bottom edge.
    /// Centered in the middle of the ui element.
    pub local_y: f32,
    /// Z order, entities with a lower Z order will be rendered on top of entities with a higher
    /// Z order.
    pub local_z: f32,
    /// The width of this UI element in pixel.
    pub width: f32,
    /// The height of this UI element in pixel.
    pub height: f32,
    /// The UI element tab order.  When the player presses tab the UI focus will shift to the
    /// UI element with the next highest tab order, or if another element with the same tab_order
    /// as this one exists they are ordered according to Entity creation order.  Shift-tab walks
    /// this ordering backwards.
    pub tab_order: i32,
    /// Global x position set by the `UiParentSystem` and `UiLayoutSystem` systems.
    pub global_x: f32,
    /// Global y position set by the `UiParentSystem` and `UiLayoutSystem` systems.
    pub global_y: f32,
    /// Global z position set by the `UiParentSystem` and `UiLayoutSystem` systems.
    pub global_z: f32,
    /// The scale mode indicates if the position is in pixel or is relative (%) (WIP!) to the parent's size.
    pub scale_mode: ScaleMode,
    /// Indicates if actions on the ui can go through this element.
    /// If set to false, the element will behaves as if it was transparent and will let events go to
    /// the next element (for example, the text on a button).
    pub opaque: bool,
    /// A private field to keep this from being initialized without new.
    pd: PhantomData<u8>,
}

impl UiTransform {
    /// Creates a new UiTransform.
    /// By default, it is considered opaque.
    pub fn new(
        id: String,
        x: f32,
        y: f32,
        z: f32,
        width: f32,
        height: f32,
        tab_order: i32,
    ) -> UiTransform {
        UiTransform {
            id,
            local_x: x,
            local_y: y,
            local_z: z,
            width,
            height,
            tab_order,
            global_x: x,
            global_y: y,
            global_z: z,
            scale_mode: ScaleMode::Pixel,
            opaque: true,
            pd: PhantomData,
        }
    }
    /// Checks if the input position is in the UiTransform rectangle.
    /// Uses local coordinates (ignores layouting).
    pub fn position_inside_local(&self, x: f32, y: f32) -> bool {
        x > self.local_x - self.width / 2.0 && y > self.local_y - self.height / 2.0
            && x < self.local_x + self.width / 2.0 && y < self.local_y + self.height / 2.0
    }

    /// Checks if the input position is in the UiTransform rectangle.
    pub fn position_inside(&self, x: f32, y: f32) -> bool {
        x > self.global_x - self.width / 2.0 && y > self.global_y - self.height / 2.0
            && x < self.global_x + self.width / 2.0 && y < self.global_y + self.height / 2.0
    }

    /// Currently unused. Will be implemented in a future PR.
    pub fn as_percent(mut self) -> Self {
        self.scale_mode = ScaleMode::Percent;
        self
    }

    /// Sets the opaque variable to false, allowing ui events to go through this ui element.
    pub fn as_transparent(mut self) -> Self {
        self.opaque = false;
        self
    }
}

impl Component for UiTransform {
    type Storage = FlaggedStorage<Self, DenseVecStorage<Self>>;
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn inside_local() {
        let tr = UiTransform::new("".to_string(), 0.0, 0.0, 0.0, 1.0, 1.0, 0);
        let pos = (-0.49, 0.20);
        assert!(tr.position_inside_local(pos.0, pos.1));
        let pos = (-1.49, 1.20);
        assert!(!tr.position_inside_local(pos.0, pos.1));
    }

    #[test]
    fn inside_global() {
        let tr = UiTransform::new("".to_string(), 0.0, 0.0, 0.0, 1.0, 1.0, 0);
        let pos = (-0.49, 0.20);
        assert!(tr.position_inside(pos.0, pos.1));
        let pos = (-1.49, 1.20);
        assert!(!tr.position_inside(pos.0, pos.1));
    }
}