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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
// Pushrod Widgets
// Properties
//
// 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 sdl2::pixels::Color;
use std::collections::HashMap;
use std::u32;

pub const PROPERTY_TEXT: u32 = 1;
pub const PROPERTY_GROUP_ID: u32 = 2;
pub const PROPERTY_MENU_ITEM_ID: u32 = 3;
pub const PROPERTY_TEXT_JUSTIFICATION: u32 = 4;
pub const PROPERTY_MAIN_COLOR: u32 = 5;
pub const PROPERTY_BORDER_COLOR: u32 = 6;
pub const PROPERTY_BORDER_WIDTH: u32 = 7;
pub const PROPERTY_FONT_NAME: u32 = 8;
pub const PROPERTY_FONT_SIZE: u32 = 9;
pub const PROPERTY_FONT_STYLE: u32 = 10;
pub const PROPERTY_FONT_COLOR: u32 = 11;
pub const PROPERTY_IMAGE_POSITION: u32 = 12;
pub const PROPERTY_IMAGE_FILENAME: u32 = 13;
pub const PROPERTY_IMAGE_SCALED: u32 = 14;
pub const PROPERTY_PROGRESS: u32 = 15;
pub const PROPERTY_PROGRESS_COLOR: u32 = 16;
pub const PROPERTY_GRID_SPACING: u32 = 17;
pub const PROPERTY_GRID_COLOR: u32 = 18;
pub const PROPERTY_GRID_CONNECTED: u32 = 19;
pub const PROPERTY_MIN_VALUE: u32 = 20;
pub const PROPERTY_MAX_VALUE: u32 = 21;
pub const PROPERTY_STEP_VALUE: u32 = 22;
pub const PROPERTY_GROUP_BACKGROUND_COLOR: u32 = 23;
pub const PROPERTY_TOGGLED: u32 = 24;

pub const PROPERTY_ID: u32 = 100;
pub const PROPERTY_INVALIDATED: u32 = 101;
pub const PROPERTY_HIDDEN: u32 = 102;
pub const PROPERTY_ORIGIN: u32 = 103;
pub const PROPERTY_SIZE: u32 = 104;
pub const PROPERTY_DISABLED: u32 = 105;
pub const PROPERTY_NEEDS_LAYOUT: u32 = 106;

/// Left-most justification of text
pub const TEXT_JUSTIFY_LEFT: i32 = 1;

/// Centered justification of text
pub const TEXT_JUSTIFY_CENTER: i32 = 2;

/// Right-most justification of text
pub const TEXT_JUSTIFY_RIGHT: i32 = 3;

/// Positions an image on the left-hand of the `Widget`.
pub const IMAGE_JUSTIFY_LEFT: i32 = 1;

/// Positions an image on the right-hand of the `Widget`.
pub const IMAGE_JUSTIFY_RIGHT: i32 = 2;

/// This is a structure that stores properties for Widgets, which can be used to define the object's
/// behavior.
#[derive(Debug, Clone, Default)]
pub struct WidgetProperties {
    properties: HashMap<u32, String>,
}

/// This is the implementation of the `WidgetProperties` store.  This is used by each and every
/// `Widget`, which stores information about the property.  Once each property is set, the `Widget`
/// can respond to the set action, and repaint itself, or anything similar.
impl WidgetProperties {
    /*
     * PRIVATE MEMBERS
     */
    #[inline]
    fn get_tuples(&self, property_key: u32, default_tuple: (u32, u32)) -> (u32, u32) {
        if self.properties.contains_key(&property_key) {
            let tokens: Vec<&str> = self
                .properties
                .get(&property_key)
                .unwrap()
                .split(' ')
                .collect();

            (
                u32::from_str_radix(tokens[0], 10).unwrap(),
                u32::from_str_radix(tokens[1], 10).unwrap(),
            )
        } else {
            default_tuple
        }
    }

    /*
     * PUBLIC MEMBERS
     */

    /// Sets a value for a property based on its numerical key.
    /// Returns a mutable reference to self so that commands can be chained together.
    pub fn set(&mut self, property_key: u32, property_value: String) -> &mut Self {
        self.properties.insert(property_key, property_value);
        self
    }

    /// Deletes a property value for the given numerical key.
    pub fn delete(&mut self, property_key: u32) {
        self.properties.remove(&property_key);
    }

    /// Retrieves the value for a property.
    pub fn get(&mut self, property_key: u32) -> String {
        self.properties
            .get(&property_key)
            .unwrap_or(&String::from(""))
            .clone()
    }

    /// Returns a flag indicating whether or not a property for a numerical key has been set.
    pub fn key_set(&mut self, property_key: u32) -> bool {
        self.properties.contains_key(&property_key)
    }

    /// Sets the invalidated state for the `Widget`.
    pub fn invalidate(&mut self) {
        self.set_bool(PROPERTY_INVALIDATED);
    }

    /// Stores the color for the specified key.  Format is "r g b a", as numerical values, base 10.
    /// Sets the invalidate flag afterward.
    /// Returns a mutable reference to self so that commands can be chained together.
    pub fn set_color(&mut self, property_key: u32, color: Color) -> &mut Self {
        self.set(
            property_key,
            format!("{} {} {} {}", color.r, color.g, color.b, color.a),
        );

        self.invalidate();
        self
    }

    /// Sets the size of the `Widget`.
    /// Returns a mutable reference to self so that commands can be chained together.
    pub fn set_bounds(&mut self, w: u32, h: u32) -> &mut Self {
        self.set(PROPERTY_SIZE, format!("{} {}", w, h));
        self
    }

    /// Sets the origin for the `Widget`.  Does not set the invalidate flag, as the repositioning of
    /// the `Widget` does not require a repaint.
    /// Returns a mutable reference to self so that commands can be chained together.
    pub fn set_origin(&mut self, x: u32, y: u32) -> &mut Self {
        self.set(PROPERTY_ORIGIN, format!("{} {}", x, y));
        self
    }

    /// Sets a boolean for a given property key.
    /// Returns a mutable reference to self so that commands can be chained together.
    pub fn set_bool(&mut self, property_key: u32) -> &mut Self {
        self.set(property_key, String::from("1"));
        self
    }

    /// Sets a numeric value to a given property key.
    /// Returns a mutable reference to self so that commands can be chained together.
    pub fn set_value(&mut self, property_key: u32, value: i32) -> &mut Self {
        self.set(property_key, format!("{}", value));
        self
    }

    /// Retrieves a color based on the given property key.  If the color cannot be found, the
    /// `default_color` specified will be returned.
    pub fn get_color(&self, property_key: u32, default_color: Color) -> Color {
        if self.properties.contains_key(&property_key) {
            let tokens: Vec<u8> = self
                .properties
                .get(&property_key)
                .unwrap()
                .split(' ')
                .map(|x| (u32::from_str_radix(x, 10).unwrap()) as u8)
                .collect();

            Color::RGBA(tokens[0], tokens[1], tokens[2], tokens[3])
        } else {
            default_color
        }
    }

    /// Retrieves the stored bounds as a tuple.  If the bounds cannot be found, invisible bounds
    /// are returned (0x0).
    pub fn get_bounds(&self) -> (u32, u32) {
        self.get_tuples(PROPERTY_SIZE, (0_u32, 0_u32))
    }

    /// Retrieves the origin of the `Widget`.  If the origin cannot be found, an origin of 0x0 is
    /// returned.
    pub fn get_origin(&self) -> (u32, u32) {
        self.get_tuples(PROPERTY_ORIGIN, (0_u32, 0_u32))
    }

    /// Retrieves the boolean value for a specified property.  If the property has not been set
    /// with `set_bool`, the return will be `false`.  Otherwise, if the specified key exists, and
    /// the value is set to `1`, the return value will be `true`.
    pub fn get_bool(&self, property_key: u32) -> bool {
        self.properties.contains_key(&property_key)
            && self.properties.get(&property_key).unwrap() == "1"
    }

    /// Retrieves a numeric value assigned to a property as an `i32` value.
    pub fn get_value(&self, property_key: u32) -> i32 {
        i32::from_str_radix(
            self.properties
                .get(&property_key)
                .unwrap_or(&String::from("0")),
            10,
        )
        .unwrap()
    }
}