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
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
use super::Control;
use error::UIError;
use libc::c_int;
use std::ffi::{CStr, CString};
use std::mem;
use ui::UI;
use ui_sys::{self, uiBox, uiControl, uiGroup, uiSeparator, uiTab, uiGrid, uiAlign, uiAt};

/// Defines the ways in which the children of boxes can be layed out.
pub enum LayoutStrategy {
    /// Make the control the minimum possible size to contain its content
    Compact,
    /// Make the control expand to its maximum size
    Stretchy,
}

define_control! {
    /// Lays out its children vertically.
    rust_type: VerticalBox,
    sys_type: uiBox
}

define_control! {
    /// Lays out its children horizontally.
    rust_type: HorizontalBox,
    sys_type: uiBox
}

impl VerticalBox {
    /// Create a new vertical box layout.
    pub fn new(_ctx: &UI) -> VerticalBox {
        VerticalBox {
            uiBox: unsafe { ui_sys::uiNewVerticalBox() },
        }
    }
}

impl HorizontalBox {
    /// Create a new horizontal box layout.
    pub fn new(_ctx: &UI) -> HorizontalBox {
        HorizontalBox {
            uiBox: unsafe { ui_sys::uiNewHorizontalBox() },
        }
    }
}

fn append<T: Into<Control>>(b: *mut uiBox, ctx: &UI, child: T, strategy: LayoutStrategy) {
    let stretchy = match strategy {
        LayoutStrategy::Compact => false,
        LayoutStrategy::Stretchy => true,
    };
    let control = child.into();
    unsafe {
        assert!(ctx.parent_of(control.clone()).is_none());
        ui_sys::uiBoxAppend(b, control.ui_control, stretchy as c_int)
    }
}

fn padded(b: *mut uiBox, _ctx: &UI) -> bool {
    unsafe { ui_sys::uiBoxPadded(b) != 0 }
}

fn set_padded(b: *mut uiBox, padded: bool, _ctx: &UI) {
    unsafe { ui_sys::uiBoxSetPadded(b, padded as c_int) }
}

impl VerticalBox {
    /// Add a control to the end of the box, sized by the given layout strategy.
    pub fn append<T: Into<Control>>(&mut self, _ctx: &UI, child: T, strategy: LayoutStrategy) {
        append(self.uiBox, _ctx, child, strategy)
    }

    /// Determine whenther the box provides padding around its children.
    pub fn padded(&self, _ctx: &UI) -> bool {
        padded(self.uiBox, _ctx)
    }

    /// Set whether or not the box should provide padding around its children.
    pub fn set_padded(&mut self, _ctx: &UI, padded: bool) {
        set_padded(self.uiBox, padded, _ctx)
    }
}

impl HorizontalBox {
    /// Add a control to the end of the box, sized by the given layout strategy.
    pub fn append<T: Into<Control>>(&mut self, _ctx: &UI, child: T, strategy: LayoutStrategy) {
        append(self.uiBox, _ctx, child, strategy)
    }

    /// Determine whenther the box provides padding around its children.
    pub fn padded(&self, _ctx: &UI) -> bool {
        padded(self.uiBox, _ctx)
    }

    /// Set whether or not the box should provide padding around its children.
    pub fn set_padded(&mut self, _ctx: &UI, padded: bool) {
        set_padded(self.uiBox, padded, _ctx)
    }
}

define_control! {
    /// Group of tabs, each of which shows a different sub-control.
    rust_type: TabGroup,
    sys_type: uiTab
}

define_control! {
    /// Collects controls together, with (optionally) a margin and/or title.
    rust_type: Group,
    sys_type: uiGroup
}

impl Group {
    /// Create a new group with the given title.
    pub fn new(_ctx: &UI, title: &str) -> Group {
        let mut group = unsafe {
            let c_string = CString::new(title.as_bytes().to_vec()).unwrap();
            Group::from_raw(ui_sys::uiNewGroup(c_string.as_ptr()))
        };
        group.set_margined(_ctx, true);
        group
    }

    /// Get a copy of the current group title.
    pub fn title(&self, _ctx: &UI) -> String {
        unsafe {
            CStr::from_ptr(ui_sys::uiGroupTitle(self.uiGroup))
                .to_string_lossy()
                .into_owned()
        }
    }

    /// Get a reference to the existing group title.
    pub fn title_ref(&self, _ctx: &UI) -> &CStr {
        unsafe { CStr::from_ptr(ui_sys::uiGroupTitle(self.uiGroup)) }
    }

    // Set the group's title.
    pub fn set_title(&mut self, _ctx: &UI, title: &str) {
        unsafe {
            let c_string = CString::new(title.as_bytes().to_vec()).unwrap();
            ui_sys::uiGroupSetTitle(self.uiGroup, c_string.as_ptr())
        }
    }

    // Set the group's child widget.
    pub fn set_child<T: Into<Control>>(&mut self, _ctx: &UI, child: T) {
        unsafe { ui_sys::uiGroupSetChild(self.uiGroup, child.into().ui_control) }
    }

    // Check whether or not the group draws a margin.
    pub fn margined(&self, _ctx: &UI) -> bool {
        unsafe { ui_sys::uiGroupMargined(self.uiGroup) != 0 }
    }

    // Set whether or not the group draws a margin.
    pub fn set_margined(&mut self, _ctx: &UI, margined: bool) {
        unsafe { ui_sys::uiGroupSetMargined(self.uiGroup, margined as c_int) }
    }
}

impl TabGroup {
    /// Create a new, empty group of tabs.
    pub fn new(_ctx: &UI) -> TabGroup {
        unsafe { TabGroup::from_raw(ui_sys::uiNewTab()) }
    }

    /// Add the given control as a new tab in the tab group with the given name.
    ///
    /// Returns the number of tabs in the group after adding the new tab.
    pub fn append<T: Into<Control>>(&mut self, _ctx: &UI, name: &str, control: T) -> u64 {
        let control = control.into();
        unsafe {
            let c_string = CString::new(name.as_bytes().to_vec()).unwrap();
            ui_sys::uiTabAppend(self.uiTab, c_string.as_ptr(), control.ui_control);
            ui_sys::uiTabNumPages(self.uiTab) as u64
        }
    }

    /// Add the given control before the given index in the tab group, as a new tab with a given name.
    ///
    /// Returns the number of tabs in the group after adding the new tab.
    pub fn insert_at<T: Into<Control>>(
        &mut self,
        _ctx: &UI,
        name: &str,
        before: u64,
        control: T,
    ) -> u64 {
        unsafe {
            let c_string = CString::new(name.as_bytes().to_vec()).unwrap();
            ui_sys::uiTabInsertAt(
                self.uiTab,
                c_string.as_ptr(),
                before,
                control.into().ui_control,
            );
            ui_sys::uiTabNumPages(self.uiTab) as u64
        }
    }

    /// Remove the control at the given index in the tab group.
    ///
    /// Returns the number of tabs in the group after removing the tab, or an error if that index was out of bounds.
    ///
    /// NOTE: This will leak the deleted control! We have no way of actually getting it
    /// to decrement its reference count per `libui`'s UI as of today, unless we maintain a
    /// separate list of children ourselves…
    pub fn delete(&mut self, _ctx: &UI, index: u64) -> Result<u64, UIError> {
        let n = unsafe { ui_sys::uiTabNumPages(self.uiTab) as u64 };
        if index < n {
            unsafe { ui_sys::uiTabDelete(self.uiTab, index) };
            Ok(n)
        } else {
            Err(UIError::TabGroupIndexOutOfBounds { index: index, n: n })
        }
    }

    /// Determine whether or not the tab group provides margins around its children.
    pub fn margined(&self, _ctx: &UI, page: u64) -> bool {
        unsafe { ui_sys::uiTabMargined(self.uiTab, page) != 0 }
    }

    /// Set whether or not the tab group provides margins around its children.
    pub fn set_margined(&mut self, _ctx: &UI, page: u64, margined: bool) {
        unsafe { ui_sys::uiTabSetMargined(self.uiTab, page, margined as c_int) }
    }
}

define_control!{
    /// Horizontal line, to seperate things visually.
    rust_type: HorizontalSeparator,
    sys_type: uiSeparator
}

impl HorizontalSeparator {
    pub fn new(_ctx: &UI) -> Self {
        unsafe { HorizontalSeparator::from_raw(ui_sys::uiNewHorizontalSeparator()) }
    }
}

define_control! {
    /// Seperates components with empty space.
    rust_type: Spacer,
    sys_type: uiBox
}

impl Spacer {
    pub fn new(_ctx: &UI) -> Self {
        unsafe { Spacer::from_raw(ui_sys::uiNewHorizontalBox()) }
    }
}

/// Informs a `LayoutGrid` about how a control should use available space
/// in one or both dimensions.
pub enum GridExpand {
    /// This control should not use extra space
    Neither,
    /// This control should use extra space horizontally
    Horizontal,
    /// This control should use extra space vertically
    Vertical,
    /// This control should use all available space in both dimensions
    Both
}

/// Informs a `LayoutGrid` how to align a control.
#[derive(Clone, Copy, PartialEq)]
pub enum GridAlignment {
    /// Expand to use all available space.
    Fill,
    /// Collapse toward the start of the available space.
    Start,
    /// Collapse equally on both sides of the available space.
    Center,
    /// Collapse toward the end of the available space.
    End
}

impl GridAlignment {
    fn into_ui_align(self) -> uiAlign {
        use self::GridAlignment::*;
        use self::uiAlign::*;
        match self {
            Fill => uiAlignFill,
            Start => uiAlignStart,
            Center => uiAlignCenter,
            End => uiAlignEnd 
        }
    }
}

/// Informs a `LayoutGrid` as to position a control.
#[derive(Clone, Copy, PartialEq)]
pub enum GridInsertionStrategy {
    /// Place control to left of existing control, align tops
    Leading,
    /// Place control above existing control, align left edges
    Top,
    /// Place control to right of existing control, align tops
    Trailing,
    /// Place control below existing control, align left edges
    Bottom
}

impl GridInsertionStrategy {
    fn into_ui_at(self) -> uiAt {
        use self::GridInsertionStrategy::*;
        use self::uiAt::*;
        match self {
            Leading => uiAtLeading,
            Top => uiAtTop,
            Trailing => uiAtTrailing,
            Bottom => uiAtBottom 
        }
    }
}

define_control!{
    /// Lays out its children in a grid according to insertion instructions.
    rust_type: LayoutGrid,
    sys_type: uiGrid
}


impl LayoutGrid {
    /// Creates a new `LayoutGrid`.
    pub fn new(_ctx: &UI) -> Self {
        unsafe { LayoutGrid::from_raw(ui_sys::uiNewGrid()) }
    }
    
    /// Returns `true` if the `LayoutGrid` is padded and `false` if not.
    pub fn padded(&self, _ctx: &UI) -> bool {
        if unsafe { ui_sys::uiGridPadded(self.uiGrid) } == 0 {
            true
        } else {
            false
        }
    }

    /// Sets the padding state of the `LayoutGrid`
    pub fn set_padded(&mut self, _ctx: &UI, padded: bool) {
       let v = if padded { 1 } else { 0 };

       unsafe {
           ui_sys::uiGridSetPadded(self.uiGrid, v);
       }
    }

    /// Adds a control to the `LayoutGrid`.
    pub fn append<T: Into<Control>>(&mut self, _ctx: &UI, control: T,
                                    left: i32, height: i32,
                                    xspan: i32, yspan: i32,
                                    expand: GridExpand,
                                    halign: GridAlignment, valign: GridAlignment) {
        let (hexpand, vexpand) = match expand {
            GridExpand::Neither => (0, 0),
            GridExpand::Horizontal => (1, 0),
            GridExpand::Vertical => (0, 1),
            GridExpand::Both => (1, 1),
        };
        unsafe { 
            ui_sys::uiGridAppend(
                self.uiGrid, control.into().ui_control, left, height, xspan, yspan,
                hexpand, halign.into_ui_align(), vexpand, valign.into_ui_align()
            );
        }    
    }

    /// Inserts a control in to the `LayoutGrid` relative to an existing control.
    pub fn insert_at<T: Into<Control>, U: Into<Control>>(&mut self, _ctx: &UI, 
                                    control: T, existing:U, at: GridInsertionStrategy,
                                    left: i32, height: i32,
                                    xspan: i32, yspan: i32,
                                    expand: GridExpand,
                                    halign: GridAlignment, valign: GridAlignment){
        
        let (hexpand, vexpand) = match expand {
            GridExpand::Neither => (0, 0),
            GridExpand::Horizontal => (1, 0),
            GridExpand::Vertical => (0, 1),
            GridExpand::Both => (1, 1),
        };
        unsafe {
            ui_sys::uiGridInsertAt(
                self.uiGrid, control.into().ui_control, existing.into().ui_control,
                at.into_ui_at(), left, height, xspan, yspan,
                hexpand, halign.into_ui_align(), vexpand, valign.into_ui_align()
            );
        }
    } 
}