fltk/
group.rs

1use crate::enums::{Align, Color, FrameType};
2use crate::prelude::*;
3use crate::utils::FlString;
4use crate::widget::Widget;
5use fltk_sys::group::*;
6use std::{
7    ffi::{CStr, CString},
8    mem,
9    ops::Range,
10    sync::atomic::{AtomicBool, Ordering},
11};
12
13static DEBUG: AtomicBool = AtomicBool::new(false);
14
15/// Creates a group widget
16#[derive(Debug)]
17pub struct Group {
18    inner: crate::widget::WidgetTracker,
19    is_derived: bool,
20}
21
22crate::macros::widget::impl_widget_ext!(Group, Fl_Group);
23crate::macros::widget::impl_widget_base!(Group, Fl_Group);
24crate::macros::widget::impl_widget_default!(Group);
25crate::macros::group::impl_group_ext!(Group, Fl_Group);
26
27impl Group {
28    #[deprecated(since = "1.2.18", note = "please use `try_current` instead")]
29    /// Get the current group
30    pub fn current() -> Group {
31        unsafe {
32            let ptr = Fl_Group_current();
33            assert!(!ptr.is_null());
34            Group::from_widget_ptr(ptr as _)
35        }
36    }
37
38    /// Tries to get the current group
39    pub fn try_current() -> Option<Group> {
40        unsafe {
41            let ptr = Fl_Group_current();
42            if ptr.is_null() {
43                None
44            } else {
45                Some(Group::from_widget_ptr(ptr as _))
46            }
47        }
48    }
49
50    /// Sets the current GroupExt widget which will take children
51    pub fn set_current(grp: Option<&impl GroupExt>) {
52        unsafe {
53            if let Some(grp) = grp {
54                Fl_Group_set_current(grp.as_widget_ptr() as _)
55            } else {
56                Fl_Group_set_current(std::ptr::null_mut())
57            }
58        }
59    }
60}
61
62/// Creates an overlay group widget which resizes itself to cover its own size
63#[derive(Debug)]
64pub struct OverlayGroup {
65    inner: crate::widget::WidgetTracker,
66    is_derived: bool,
67}
68
69crate::macros::widget::impl_widget_ext!(OverlayGroup, Fl_Overlay_Group);
70crate::macros::widget::impl_widget_base!(OverlayGroup, Fl_Overlay_Group);
71crate::macros::widget::impl_widget_default!(OverlayGroup);
72crate::macros::group::impl_group_ext!(OverlayGroup, Fl_Overlay_Group);
73
74/// Creates a widget pack
75#[derive(Debug)]
76pub struct Pack {
77    inner: crate::widget::WidgetTracker,
78    is_derived: bool,
79}
80
81crate::macros::widget::impl_widget_ext!(Pack, Fl_Pack);
82crate::macros::widget::impl_widget_base!(Pack, Fl_Pack);
83crate::macros::widget::impl_widget_default!(Pack);
84crate::macros::group::impl_group_ext!(Pack, Fl_Pack);
85
86/// Defines pack types
87#[repr(i32)]
88#[derive(Debug, Copy, Clone, PartialEq, Eq)]
89pub enum PackType {
90    /// Vertical layout pack
91    Vertical = 0,
92    /// Horizontal layout pack
93    Horizontal = 1,
94}
95
96crate::macros::widget::impl_widget_type!(PackType);
97
98impl Pack {
99    /// Get the spacing of the pack
100    pub fn spacing(&self) -> i32 {
101        unsafe { Fl_Pack_spacing(self.inner.widget() as _) }
102    }
103
104    /// Set the spacing of the pack
105    pub fn set_spacing(&mut self, spacing: i32) {
106        unsafe {
107            Fl_Pack_set_spacing(self.inner.widget() as _, spacing);
108        }
109    }
110
111    /// Layout the children of the pack automatically.
112    /// Must be called on existing children
113    pub fn auto_layout(&mut self) {
114        let children = self.children();
115        if children == 0 {
116            return;
117        }
118        let spacing = self.spacing() * (children - 1);
119        let t = self.get_type::<PackType>();
120        let w = (self.width() - spacing) / children;
121        let h = (self.height() - spacing) / children;
122
123        for i in 0..children {
124            let mut c = self.child(i).unwrap();
125            let c_w = c.width();
126            let c_h = c.height();
127            if t == PackType::Vertical {
128                c.set_size(c_w, h);
129            } else {
130                c.set_size(w, c_h);
131            }
132        }
133    }
134}
135
136/// Creates a scroll group
137#[derive(Debug)]
138pub struct Scroll {
139    inner: crate::widget::WidgetTracker,
140    is_derived: bool,
141}
142
143crate::macros::widget::impl_widget_ext!(Scroll, Fl_Scroll);
144crate::macros::widget::impl_widget_base!(Scroll, Fl_Scroll);
145crate::macros::widget::impl_widget_default!(Scroll);
146crate::macros::group::impl_group_ext!(Scroll, Fl_Scroll);
147
148/// Defines Scroll types
149#[repr(i32)]
150#[derive(Debug, Copy, Clone, PartialEq, Eq)]
151pub enum ScrollType {
152    /// Never show bars
153    None = 0,
154    /// Show vertical bar
155    Horizontal = 1,
156    /// Show vertical bar
157    Vertical = 2,
158    /// Show both horizontal and vertical bars
159    Both = 3,
160    /// Always show bars
161    AlwaysOn = 4,
162    /// Show horizontal bar always
163    HorizontalAlways = 5,
164    /// Show vertical bar always
165    VerticalAlways = 6,
166    /// Always show both horizontal and vertical bars
167    BothAlways = 7,
168}
169
170crate::macros::widget::impl_widget_type!(ScrollType);
171
172impl Scroll {
173    /// Returns the vertical scrollbar
174    pub fn scrollbar(&self) -> crate::valuator::Scrollbar {
175        unsafe {
176            let ptr = Fl_Scroll_scrollbar(self.inner.widget() as _);
177            assert!(!ptr.is_null());
178            crate::valuator::Scrollbar::from_widget_ptr(ptr as *mut fltk_sys::widget::Fl_Widget)
179        }
180    }
181
182    /// Returns the horizontal scrollbar
183    pub fn hscrollbar(&self) -> crate::valuator::Scrollbar {
184        unsafe {
185            let ptr = Fl_Scroll_hscrollbar(self.inner.widget() as _);
186            assert!(!ptr.is_null());
187            crate::valuator::Scrollbar::from_widget_ptr(ptr as *mut fltk_sys::widget::Fl_Widget)
188        }
189    }
190
191    /// Returns the x position
192    pub fn xposition(&self) -> i32 {
193        unsafe { Fl_Scroll_xposition(self.inner.widget() as _) }
194    }
195
196    /// Returns the y position
197    pub fn yposition(&self) -> i32 {
198        unsafe { Fl_Scroll_yposition(self.inner.widget() as _) }
199    }
200
201    /// Scrolls to `x` and `y`
202    pub fn scroll_to(&mut self, x: i32, y: i32) {
203        unsafe { Fl_Scroll_scroll_to(self.inner.widget() as _, x, y) }
204    }
205
206    /// Gets the scrollbar size
207    pub fn scrollbar_size(&self) -> i32 {
208        unsafe { Fl_Scroll_scrollbar_size(self.inner.widget() as _) }
209    }
210
211    /// Sets the scrollbar size
212    pub fn set_scrollbar_size(&mut self, new_size: i32) {
213        unsafe { Fl_Scroll_set_scrollbar_size(self.inner.widget() as _, new_size) }
214    }
215
216    /// Auto layout a scroll widget
217    pub fn auto_layout(&mut self) {
218        self.resize_callback(|t, x, y, w, h| {
219            if t.children() == 1 {
220                if let Some(mut c) = t.child(0) {
221                    c.resize(x, y, w, h);
222                }
223            }
224        });
225    }
226}
227
228/// Defines how Tabs handle overflow
229#[repr(i32)]
230#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
231pub enum TabsOverflow {
232    /// Compress tabs
233    Compress = 0,
234    /// Clip tabs
235    Clip,
236    /// Create a pulldown
237    Pulldown,
238    /// Drag tabs
239    Drag,
240}
241
242/// Creates a tab which can contain widgets
243#[derive(Debug)]
244pub struct Tabs {
245    inner: crate::widget::WidgetTracker,
246    is_derived: bool,
247}
248
249crate::macros::widget::impl_widget_ext!(Tabs, Fl_Tabs);
250crate::macros::widget::impl_widget_base!(Tabs, Fl_Tabs);
251crate::macros::widget::impl_widget_default!(Tabs);
252crate::macros::group::impl_group_ext!(Tabs, Fl_Tabs);
253
254impl Tabs {
255    /// Gets the currently visible group
256    pub fn value(&self) -> Option<impl GroupExt> {
257        unsafe {
258            let ptr = Fl_Tabs_value(self.inner.widget() as _);
259            if ptr.is_null() {
260                None
261            } else {
262                Some(Group::from_widget_ptr(
263                    ptr as *mut fltk_sys::widget::Fl_Widget,
264                ))
265            }
266        }
267    }
268
269    /// Sets the currently visible group
270    /// # Errors
271    /// Errors when the value can't be set for the group widget
272    pub fn set_value<Grp: GroupExt>(&mut self, w: &Grp) -> Result<(), FltkError> {
273        unsafe {
274            match Fl_Tabs_set_value(
275                self.inner.widget() as _,
276                w.as_widget_ptr() as *mut fltk_sys::group::Fl_Widget,
277            ) {
278                0 => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
279                _ => Ok(()),
280            }
281        }
282    }
283
284    /// Returns the tab group for the tab the user has currently down-clicked
285    pub fn push(&self) -> Option<impl GroupExt> {
286        unsafe {
287            let ptr = Fl_Tabs_push(self.inner.widget() as _);
288            if ptr.is_null() {
289                None
290            } else {
291                Some(Group::from_widget_ptr(
292                    ptr as *mut fltk_sys::widget::Fl_Widget,
293                ))
294            }
295        }
296    }
297
298    /// This is called by the tab widget's handle() method to set the tab group widget the user last pushed
299    /// # Errors
300    /// Errors if `set_push` can't be set for the group widget
301    pub fn set_push<Grp: GroupExt>(&mut self, w: &Grp) -> Result<(), FltkError> {
302        unsafe {
303            match Fl_Tabs_set_push(
304                self.inner.widget() as _,
305                w.as_widget_ptr() as *mut fltk_sys::group::Fl_Widget,
306            ) {
307                0 => Err(FltkError::Internal(FltkErrorKind::FailedOperation)),
308                _ => Ok(()),
309            }
310        }
311    }
312
313    /// Returns the position and size available to be used by its children
314    pub fn client_area(&self) -> (i32, i32, i32, i32) {
315        unsafe {
316            fltk_sys::fl::Fl_open_display();
317            let mut i1 = 0;
318            let mut i2 = 0;
319            let mut i3 = 0;
320            let mut i4 = 0;
321            Fl_Tabs_client_area(self.inner.widget() as _, &mut i1, &mut i2, &mut i3, &mut i4);
322            (i1, i2, i3, i4)
323        }
324    }
325
326    /// Sets the tab label alignment
327    pub fn set_tab_align(&mut self, a: Align) {
328        unsafe { Fl_Tabs_set_tab_align(self.inner.widget() as _, a.bits()) }
329    }
330
331    /// Gets the tab label alignment.
332    pub fn tab_align(&self) -> Align {
333        unsafe { mem::transmute(Fl_Tabs_tab_align(self.inner.widget() as _)) }
334    }
335
336    /// Auto layout a tabs widget
337    pub fn auto_layout(&mut self) {
338        for c in self.clone().into_iter() {
339            if let Some(mut c) = c.as_group() {
340                c.resize(self.x(), self.y() + 30, self.w(), self.h() - 30);
341            }
342        }
343        self.resize_callback(|t, x, y, w, h| {
344            for c in t.clone().into_iter() {
345                if let Some(mut c) = c.as_group() {
346                    c.resize(x, y + 30, w, h - 30);
347                }
348            }
349        });
350    }
351
352    /// Sets how the Tabs handles overflow
353    pub fn handle_overflow(&mut self, ov: TabsOverflow) {
354        unsafe { Fl_Tabs_handle_overflow(self.inner.widget() as _, ov as i32) }
355    }
356}
357
358/// Creates a tile which can contain widgets. For the tiling to work correctly, the children of a Tile must cover the entire area of the widget, but not overlap. This means that all children must touch each other at their edges, and no gaps can be left inside the Tile.
359/// More info can be found [here](https://www.fltk.org/doc-1.4/classFl__Tile.html#details)
360#[derive(Debug)]
361pub struct Tile {
362    inner: crate::widget::WidgetTracker,
363    is_derived: bool,
364}
365
366crate::macros::widget::impl_widget_ext!(Tile, Fl_Tile);
367crate::macros::widget::impl_widget_base!(Tile, Fl_Tile);
368crate::macros::widget::impl_widget_default!(Tile);
369crate::macros::group::impl_group_ext!(Tile, Fl_Tile);
370
371impl Tile {
372    /**
373    Drags the intersection at (\p oldx,\p oldy) to (\p newx,\p newy).
374
375    This redraws all the necessary children.
376
377    If no size ranges are set, the new intersection position is limited to the
378    size of the tile group. The resizable() option is not taken into account here.
379
380    If size ranges are set, the actual new position of the intersection will
381    depend on the size range of every individual child. No child will be smaller
382    than their minw and minh. After the new position is found, `move_intersection()`
383    will call `init_sizes()`. The `resizable()` range is ignored.
384
385    params `oldx`, `oldy` move the intersection at this coordinate, pass zero to
386        disable drag in that direction.
387    params `newx`, `newy` move the intersection as close to this new coordinate
388        as possible
389    */
390    pub fn move_intersection(&mut self, oldx: i32, oldy: i32, newx: i32, newy: i32) {
391        unsafe {
392            Fl_Tile_move_intersection(self.inner.widget() as _, oldx, oldy, newx, newy);
393        }
394    }
395
396    /// Set the allowed size range for the child at the given index
397    pub fn size_range_by_index(&mut self, idx: i32, minw: i32, minh: i32, maxw: i32, maxh: i32) {
398        unsafe {
399            Fl_Tile_size_range_by_index(self.inner.widget() as _, idx, minw, minh, maxw, maxh)
400        }
401    }
402
403    /// Set the allowed size range for the given child widget
404    pub fn size_range_by_child<W: WidgetExt>(
405        &mut self,
406        w: &W,
407        minw: i32,
408        minh: i32,
409        maxw: i32,
410        maxh: i32,
411    ) {
412        unsafe {
413            Fl_Tile_size_range_by_child(
414                self.inner.widget() as _,
415                w.as_widget_ptr() as _,
416                minw,
417                minh,
418                maxw,
419                maxh,
420            )
421        }
422    }
423}
424
425/// Creates a wizard widget
426#[derive(Debug)]
427pub struct Wizard {
428    inner: crate::widget::WidgetTracker,
429    is_derived: bool,
430}
431
432crate::macros::widget::impl_widget_ext!(Wizard, Fl_Wizard);
433crate::macros::widget::impl_widget_base!(Wizard, Fl_Wizard);
434crate::macros::widget::impl_widget_default!(Wizard);
435crate::macros::group::impl_group_ext!(Wizard, Fl_Wizard);
436
437impl Wizard {
438    /// Gets the next view of the wizard
439    pub fn next(&mut self) {
440        unsafe { Fl_Wizard_next(self.inner.widget() as _) }
441    }
442
443    /// Gets the previous view of the wizard
444    pub fn prev(&mut self) {
445        unsafe { Fl_Wizard_prev(self.inner.widget() as _) }
446    }
447
448    #[deprecated(since = "1.2.18", note = "please use `try_current_widget` instead")]
449    /// Gets the underlying widget of the current view
450    pub fn current_widget(&self) -> Widget {
451        unsafe {
452            let ptr = Fl_Wizard_value(self.inner.widget() as _) as *mut fltk_sys::widget::Fl_Widget;
453            assert!(!ptr.is_null());
454            Widget::from_widget_ptr(ptr)
455        }
456    }
457
458    /// Gets the underlying widget of the current view
459    pub fn try_current_widget(&self) -> Option<impl WidgetExt> {
460        unsafe {
461            let ptr = Fl_Wizard_value(self.inner.widget() as _) as *mut fltk_sys::widget::Fl_Widget;
462            if ptr.is_null() {
463                None
464            } else {
465                Some(Widget::from_widget_ptr(ptr))
466            }
467        }
468    }
469
470    /// Sets the underlying widget of the current view
471    pub fn set_current_widget<W: WidgetExt>(&mut self, w: &W) {
472        unsafe {
473            Fl_Wizard_set_value(
474                self.inner.widget() as _,
475                w.as_widget_ptr() as *mut fltk_sys::group::Fl_Widget,
476            )
477        }
478    }
479
480    /// Set the index of the wizard
481    pub fn set_index(&mut self, idx: i32) -> Result<(), FltkError> {
482        if let Some(w) = self.child(idx) {
483            self.set_current_widget(&w);
484            Ok(())
485        } else {
486            Err(FltkError::Unknown("Index out of bounds".to_string()))
487        }
488    }
489
490    /// Get the index of the wizard
491    pub fn index(&self) -> Option<i32> {
492        Some(self.find(&self.try_current_widget()?))
493    }
494}
495
496/// Creates a color chooser widget
497#[derive(Debug)]
498pub struct ColorChooser {
499    inner: crate::widget::WidgetTracker,
500    is_derived: bool,
501}
502
503crate::macros::widget::impl_widget_ext!(ColorChooser, Fl_Color_Chooser);
504crate::macros::widget::impl_widget_base!(ColorChooser, Fl_Color_Chooser);
505crate::macros::widget::impl_widget_default!(ColorChooser);
506crate::macros::group::impl_group_ext!(ColorChooser, Fl_Color_Chooser);
507
508impl ColorChooser {
509    /// Return the rgb color
510    pub fn rgb_color(&self) -> (u8, u8, u8) {
511        unsafe {
512            let r = (Fl_Color_Chooser_r(self.inner.widget() as _) * 255.0) as u8;
513            let g = (Fl_Color_Chooser_g(self.inner.widget() as _) * 255.0) as u8;
514            let b = (Fl_Color_Chooser_b(self.inner.widget() as _) * 255.0) as u8;
515            (r, g, b)
516        }
517    }
518
519    /// Return the hex color
520    pub fn hex_color(&self) -> u32 {
521        let (r, g, b) = self.rgb_color();
522        crate::utils::rgb2hex(r, g, b)
523    }
524
525    /// Set the base color of the ColorChooser. Returns an error on failure to change the color (wrong input)
526    pub fn set_rgb(&mut self, r: u8, g: u8, b: u8) -> Result<(), FltkError> {
527        unsafe {
528            let ret = Fl_Color_Chooser_set_rgb(
529                self.inner.widget() as _,
530                r as f64 / 255.0,
531                g as f64 / 255.0,
532                b as f64 / 255.0,
533            );
534            if ret == 1 {
535                Ok(())
536            } else {
537                Err(FltkError::Internal(FltkErrorKind::FailedOperation))
538            }
539        }
540    }
541
542    /// Set the base color of the ColorChooser. Returns an error on failure to change the color (wrong input)
543    pub fn set_tuple_rgb(&mut self, (r, g, b): (u8, u8, u8)) -> Result<(), FltkError> {
544        unsafe {
545            let ret = Fl_Color_Chooser_set_rgb(
546                self.inner.widget() as _,
547                r as f64 / 255.0,
548                g as f64 / 255.0,
549                b as f64 / 255.0,
550            );
551            if ret == 1 {
552                Ok(())
553            } else {
554                Err(FltkError::Internal(FltkErrorKind::FailedOperation))
555            }
556        }
557    }
558}
559
560crate::macros::widget::impl_widget_type!(FlexType);
561
562/// Defines Flex types
563#[repr(i32)]
564#[derive(Debug, Copy, Clone, PartialEq, Eq)]
565pub enum FlexType {
566    /// row direction
567    Column = 0,
568    /// column direction
569    Row,
570}
571
572/**
573    a Flexbox widget
574    # Example
575    ```rust,no_run
576    use fltk::{prelude::*, *};
577    fn main() {
578        let a = app::App::default();
579        let mut win = window::Window::default().with_size(400, 300);
580        let mut col = group::Flex::default().size_of_parent();
581        col.set_type(group::FlexType::Column);
582        let expanding = button::Button::default().with_label("Expanding");
583        let mut normal = button::Button::default().with_label("Normal");
584        col.set_size(&mut normal, 30);
585        col.end();
586        win.end();
587        win.show();
588        a.run().unwrap();
589    }
590    ```
591*/
592#[derive(Debug)]
593pub struct Flex {
594    inner: crate::widget::WidgetTracker,
595    is_derived: bool,
596}
597
598crate::macros::widget::impl_widget_ext!(Flex, Fl_Flex);
599crate::macros::widget::impl_widget_base!(Flex, Fl_Flex);
600crate::macros::widget::impl_widget_default!(Flex);
601crate::macros::group::impl_group_ext!(Flex, Fl_Flex);
602
603impl Flex {
604    /// Create a new Flex widget.
605    /// This code is here for backward compatibility with initial Fl_Flex code, which defaulted to Row instead of Column.
606    /// The behavior will be changed in fltk-rs version 2.
607    fn new<T: Into<Option<&'static str>>>(
608        x: i32,
609        y: i32,
610        width: i32,
611        height: i32,
612        title: T,
613    ) -> Self {
614        let mut f = <Flex as WidgetBase>::new(x, y, width, height, title).row();
615        f.set_pad(5);
616        f.debug_();
617        f
618    }
619    /// Add a widget to the Flex box
620    pub fn add<W: WidgetExt>(&mut self, widget: &W) {
621        <Self as GroupExt>::add(self, widget);
622        self.recalc();
623    }
624
625    /// Add a widget to the Flex box
626    pub fn insert<W: WidgetExt>(&mut self, widget: &W, idx: i32) {
627        <Self as GroupExt>::insert(self, widget, idx);
628        self.recalc();
629    }
630
631    /// Set the size of the widget, same as `fixed` (before it was changed in FLTK 1.4)
632    #[deprecated(since = "1.4.8", note = "please use `fixed` instead")]
633    pub fn set_size<W: WidgetExt>(&mut self, w: &W, size: i32) {
634        unsafe { Fl_Flex_set_size(self.inner.widget() as _, w.as_widget_ptr() as _, size) }
635    }
636
637    /// Set the size of the widget, same as `set_size`, but more inline with the new FLTK Fl_Flex api
638    pub fn fixed<W: WidgetExt>(&mut self, w: &W, size: i32) {
639        unsafe { Fl_Flex_set_size(self.inner.widget() as _, w.as_widget_ptr() as _, size) }
640    }
641
642    /// Debug the flex layout
643    pub fn debug(flag: bool) {
644        DEBUG.store(flag, Ordering::Release);
645    }
646
647    fn debug_(&mut self) {
648        if DEBUG.load(Ordering::Relaxed) {
649            self.set_frame(FrameType::BorderBox);
650            if self.get_type::<FlexType>() == FlexType::Row {
651                self.set_color(Color::from_rgb(200, 0, 0));
652            } else {
653                self.set_color(Color::from_rgb(0, 0, 200));
654            }
655        }
656    }
657
658    /// Set the type to be a column
659    pub fn column(mut self) -> Self {
660        self.set_type(FlexType::Column);
661        self.debug_();
662        self
663    }
664
665    /// Set the type to a row
666    pub fn row(mut self) -> Self {
667        self.set_type(FlexType::Row);
668        self.debug_();
669        self
670    }
671
672    /// Recalculate children's coords and sizes
673    pub fn recalc(&self) {
674        let mut s = self.clone();
675        s.resize(self.x(), self.y(), self.w(), self.h());
676        s.redraw();
677    }
678
679    /// Recalculate children's coords and sizes
680    pub fn layout(&self) {
681        self.recalc();
682    }
683
684    /// Set the margin
685    pub fn set_margin(&mut self, m: i32) {
686        unsafe { Fl_Flex_set_margin(self.inner.widget() as _, m) }
687    }
688
689    /// Get the margin
690    pub fn margin(&self) -> i32 {
691        unsafe { Fl_Flex_margin(self.inner.widget() as _) }
692    }
693
694    /// Set the padding
695    pub fn set_pad(&mut self, p: i32) {
696        unsafe { Fl_Flex_set_pad(self.inner.widget() as _, p) }
697    }
698
699    /// Get the padding
700    pub fn pad(&self) -> i32 {
701        unsafe { Fl_Flex_pad(self.inner.widget() as _) }
702    }
703
704    /// Set the padding
705    pub fn set_spacing(&mut self, p: i32) {
706        unsafe { Fl_Flex_set_pad(self.inner.widget() as _, p) }
707    }
708
709    /// Get the padding
710    pub fn spacing(&self) -> i32 {
711        unsafe { Fl_Flex_pad(self.inner.widget() as _) }
712    }
713
714    /// Set the margins
715    pub fn set_margins(&mut self, left: i32, top: i32, right: i32, bottom: i32) {
716        unsafe { Fl_Flex_set_margins(self.inner.widget() as _, left, top, right, bottom) }
717    }
718
719    /// Get the margins -> returns (left, top, right, bottom)
720    pub fn margins(&self) -> (i32, i32, i32, i32) {
721        let mut left = 0;
722        let mut top = 0;
723        let mut right = 0;
724        let mut bottom = 0;
725        unsafe {
726            Fl_Flex_margins(
727                self.inner.widget() as _,
728                &mut left,
729                &mut top,
730                &mut right,
731                &mut bottom,
732            );
733        }
734        (left, top, right, bottom)
735    }
736}
737
738/**
739    Defines a Vertical Grid (custom widget).
740    Requires setting the params manually using the `set_params` method, which takes the rows, columns and spacing.
741    ```rust,no_run
742    use fltk::{prelude::*, *};
743    fn main() {
744        let app = app::App::default();
745        let mut win = window::Window::default().with_size(400, 300);
746        let mut grid = group::VGrid::new(0, 0, 400, 300, "");
747        grid.set_params(3, 3, 5);
748        button::Button::default();
749        button::Button::default();
750        button::Button::default();
751        button::Button::default();
752        button::Button::default();
753        button::Button::default();
754        button::Button::default();
755        button::Button::default();
756        grid.end();
757        win.end();
758        win.show();
759        app.run().unwrap();
760    }
761    ```
762*/
763#[derive(Debug, Clone)]
764pub struct VGrid {
765    vpack: Pack,
766    rows: i32,
767    cols: i32,
768    current: i32,
769}
770
771impl Default for VGrid {
772    fn default() -> Self {
773        Self::new(0, 0, 0, 0, None)
774    }
775}
776
777impl VGrid {
778    /// Constructs a widget with the size of its parent
779    pub fn default_fill() -> Self {
780        Self::default().size_of_parent()
781    }
782
783    /// Creates a new vertical grid
784    pub fn new<T: Into<Option<&'static str>>>(x: i32, y: i32, w: i32, h: i32, label: T) -> VGrid {
785        let vpack = Pack::new(x, y, w, h, label);
786        VGrid {
787            vpack,
788            rows: 1,
789            cols: 1,
790            current: 0,
791        }
792    }
793
794    /// Sets the params for the grid
795    pub fn set_params(&mut self, rows: i32, cols: i32, spacing: i32) {
796        self.vpack.set_spacing(spacing);
797        let rows = if rows < 1 { 1 } else { rows };
798        let cols = if cols < 1 { 1 } else { cols };
799        self.rows = rows;
800        self.cols = cols;
801        for _ in 0..rows {
802            let mut p = Pack::new(0, 0, self.vpack.width(), 0, "");
803            p.set_type(PackType::Horizontal);
804            p.set_spacing(spacing);
805            p.end();
806            self.vpack.add(&p);
807        }
808    }
809
810    /// Adds widgets to the grid
811    pub fn add<W: WidgetExt>(&mut self, w: &W) {
812        debug_assert!(self.current < self.rows * self.cols);
813        let rem = (self.current - 1) / self.cols;
814        if rem < self.rows {
815            let hpack = self.vpack.child(rem).unwrap();
816            let mut hpack = unsafe { Pack::from_widget_ptr(hpack.as_widget_ptr()) };
817            hpack.end();
818            hpack.add(w);
819            hpack.auto_layout();
820            self.vpack.auto_layout();
821            self.current += 1;
822        }
823    }
824
825    /// End the grid
826    pub fn end(&mut self) {
827        use std::collections::VecDeque;
828        let children = self.vpack.children();
829        self.current = children - self.rows;
830        debug_assert!(self.current <= self.rows * self.cols);
831        let mut v = VecDeque::new();
832        for i in self.rows..children {
833            let c = self.vpack.child(i).unwrap();
834            v.push_back(c);
835        }
836        for i in 0..self.rows {
837            let hpack = self.vpack.child(i).unwrap();
838            let mut hpack = unsafe { Pack::from_widget_ptr(hpack.as_widget_ptr()) };
839            hpack.end();
840            for _j in 0..self.cols {
841                if let Some(w) = v.pop_front() {
842                    self.vpack.remove(&w);
843                    hpack.add(&w);
844                }
845                hpack.auto_layout();
846            }
847        }
848        self.vpack.auto_layout();
849    }
850}
851
852crate::widget_extends!(VGrid, Pack, vpack);
853
854/**
855    Defines a Horizontal Grid (custom widget).
856    Requires setting the params manually using the `set_params` method, which takes the rows, columns and spacing.
857    ```rust,no_run
858    use fltk::{prelude::*, *};
859    fn main() {
860        let app = app::App::default();
861        let mut win = window::Window::default().with_size(400, 300);
862        let mut grid = group::HGrid::new(0, 0, 400, 300, "");
863        grid.set_params(3, 3, 5);
864        button::Button::default();
865        button::Button::default();
866        button::Button::default();
867        button::Button::default();
868        button::Button::default();
869        button::Button::default();
870        button::Button::default();
871        button::Button::default();
872        grid.end();
873        win.end();
874        win.show();
875        app.run().unwrap();
876    }
877    ```
878*/
879#[derive(Debug, Clone)]
880pub struct HGrid {
881    hpack: Pack,
882    rows: i32,
883    cols: i32,
884    current: i32,
885}
886
887impl Default for HGrid {
888    fn default() -> Self {
889        Self::new(0, 0, 0, 0, None)
890    }
891}
892
893impl HGrid {
894    /// Constructs a widget with the size of its parent
895    pub fn default_fill() -> Self {
896        Self::default().size_of_parent()
897    }
898
899    /// Creates a new horizontal grid
900    pub fn new<T: Into<Option<&'static str>>>(x: i32, y: i32, w: i32, h: i32, label: T) -> HGrid {
901        let mut hpack = Pack::new(x, y, w, h, label);
902        hpack.set_type(PackType::Horizontal);
903        HGrid {
904            hpack,
905            rows: 1,
906            cols: 1,
907            current: 0,
908        }
909    }
910
911    /// Sets the params for the grid
912    pub fn set_params(&mut self, rows: i32, cols: i32, spacing: i32) {
913        self.hpack.set_spacing(spacing);
914        let rows = if rows < 1 { 1 } else { rows };
915        let cols = if cols < 1 { 1 } else { cols };
916        self.rows = rows;
917        self.cols = cols;
918        for _ in 0..cols {
919            let mut p = Pack::new(0, 0, 0, self.hpack.height(), "");
920            p.set_spacing(spacing);
921            p.end();
922            self.hpack.add(&p);
923        }
924    }
925
926    /// Adds widgets to the grid
927    pub fn add<W: WidgetExt>(&mut self, w: &W) {
928        debug_assert!(self.current < self.rows * self.cols);
929        let rem = (self.current - 1) / self.rows;
930        if rem < self.cols {
931            let vpack = self.hpack.child(rem).unwrap();
932            let mut vpack = unsafe { Pack::from_widget_ptr(vpack.as_widget_ptr()) };
933            vpack.end();
934            vpack.add(w);
935            vpack.auto_layout();
936            self.hpack.auto_layout();
937            self.current += 1;
938        }
939    }
940
941    /// End the grid
942    pub fn end(&mut self) {
943        use std::collections::VecDeque;
944        let children = self.hpack.children();
945        self.current = children - self.cols;
946        debug_assert!(self.current <= self.rows * self.cols);
947        let mut v = VecDeque::new();
948        for i in self.cols..children {
949            let c = self.hpack.child(i).unwrap();
950            v.push_back(c);
951        }
952        for i in 0..self.cols {
953            let vpack = self.hpack.child(i).unwrap();
954            let mut vpack = unsafe { Pack::from_widget_ptr(vpack.as_widget_ptr()) };
955            vpack.end();
956            for _j in 0..self.rows {
957                if let Some(w) = v.pop_front() {
958                    self.hpack.remove(&w);
959                    vpack.add(&w);
960                }
961                vpack.auto_layout();
962            }
963        }
964        self.hpack.auto_layout();
965    }
966}
967
968crate::widget_extends!(HGrid, Pack, hpack);
969
970/// A wrapper around a Flex column
971#[derive(Debug, Clone)]
972pub struct Column {
973    p: Flex,
974}
975
976impl Default for Column {
977    fn default() -> Self {
978        Self::new(0, 0, 0, 0, None)
979    }
980}
981
982impl Column {
983    /// Constructs a widget with the size of its parent
984    pub fn default_fill() -> Self {
985        Self::default().size_of_parent().center_of_parent()
986    }
987
988    /// Create a new column
989    pub fn new<T: Into<Option<&'static str>>>(
990        x: i32,
991        y: i32,
992        width: i32,
993        height: i32,
994        label: T,
995    ) -> Column {
996        let mut p = Flex::new(x, y, width, height, label);
997        p.set_type(FlexType::Column);
998        Column { p }
999    }
1000
1001    /// Add a widget to the column with automatic layouting
1002    pub fn add<W: WidgetExt>(&mut self, w: &W) {
1003        self.p.add(w);
1004    }
1005}
1006
1007crate::widget_extends!(Column, Flex, p);
1008
1009/// A wrapper around a Flex row
1010#[derive(Debug, Clone)]
1011pub struct Row {
1012    p: Flex,
1013}
1014
1015impl Default for Row {
1016    fn default() -> Self {
1017        Self::new(0, 0, 0, 0, None)
1018    }
1019}
1020
1021impl Row {
1022    /// Constructs a widget with the size of its parent
1023    pub fn default_fill() -> Self {
1024        Self::default().size_of_parent().center_of_parent()
1025    }
1026
1027    /// Create a new row
1028    pub fn new<T: Into<Option<&'static str>>>(
1029        x: i32,
1030        y: i32,
1031        width: i32,
1032        height: i32,
1033        label: T,
1034    ) -> Row {
1035        let mut p = Flex::new(x, y, width, height, label);
1036        p.set_type(FlexType::Row);
1037        Row { p }
1038    }
1039
1040    /// Add a widget to the row with automatic layouting
1041    pub fn add<W: WidgetExt>(&mut self, w: &W) {
1042        self.p.add(w);
1043    }
1044}
1045
1046crate::widget_extends!(Row, Flex, p);
1047
1048/// Grid range
1049pub struct GridRange {
1050    start: usize,
1051    end: usize,
1052}
1053
1054impl GridRange {
1055    /// Check the length of the GridRange
1056    pub fn len(&self) -> usize {
1057        self.end - self.start
1058    }
1059
1060    /// Check whether the GridRange is empty
1061    pub fn is_empty(&self) -> bool {
1062        self.len() == 0
1063    }
1064}
1065
1066impl From<Range<usize>> for GridRange {
1067    fn from(val: Range<usize>) -> Self {
1068        Self {
1069            start: val.start,
1070            end: val.end,
1071        }
1072    }
1073}
1074
1075impl From<usize> for GridRange {
1076    fn from(val: usize) -> Self {
1077        (val..val + 1).into()
1078    }
1079}
1080
1081bitflags::bitflags! {
1082    /// Defines alignment rules used by FLTK's Grid
1083    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1084    pub struct GridAlign: u16 {
1085        /** Align the widget at the center of the cell. */
1086        const  CENTER          = 0x0000;
1087        /** Align the widget at the top of the cell. */
1088        const  TOP             = 0x0001;
1089        /** Align the widget at the bottom of the cell. */
1090        const  BOTTOM          = 0x0002;
1091        /** Align the widget at the left side of the cell. */
1092        const  LEFT            = 0x0004;
1093        /** Align the widget at the right side of the cell. */
1094        const  RIGHT           = 0x0008;
1095        /** Stretch the widget horizontally to fill the cell. */
1096        const  HORIZONTAL      = 0x0010;
1097        /** Stretch the widget vertically to fill the cell. */
1098        const  VERTICAL        = 0x0020;
1099        /** Stretch the widget in both directions to fill the cell. */
1100        const  FILL            = 0x0030;
1101        /** Stretch the widget proportionally. */
1102        const  PROPORTIONAL    = 0x0040;
1103        /** Align the widget at the top left of the cell. */
1104        const  TOP_LEFT        =  GridAlign::TOP.bits() |  GridAlign::LEFT.bits();
1105        /** Align the widget at the top right of the cell. */
1106        const  TOP_RIGHT       =  GridAlign::TOP.bits() |  GridAlign::RIGHT.bits();
1107        /** Align the widget at the bottom left of the cell. */
1108        const  BOTTOM_LEFT     =  GridAlign::BOTTOM.bits() |  GridAlign::LEFT.bits();
1109        /** Align the widget at the bottom right of the cell. */
1110        const  BOTTOM_RIGHT    =  GridAlign::BOTTOM.bits() |  GridAlign::RIGHT.bits();
1111    }
1112}
1113/// Fltk's grid widget
1114#[derive(Debug)]
1115pub struct Grid {
1116    inner: crate::widget::WidgetTracker,
1117    is_derived: bool,
1118}
1119
1120crate::macros::widget::impl_widget_ext!(Grid, Fl_Grid);
1121crate::macros::widget::impl_widget_base!(Grid, Fl_Grid);
1122crate::macros::widget::impl_widget_default!(Grid);
1123crate::macros::group::impl_group_ext!(Grid, Fl_Grid);
1124
1125impl Grid {
1126    /// Set the layout of the grid, along with the margin and gap
1127    pub fn set_layout_ext(&mut self, rows: i32, cols: i32, margin: i32, gap: i32) {
1128        unsafe { Fl_Grid_set_layout(self.inner.widget() as _, rows, cols, margin, gap) }
1129    }
1130    /// Set the layout of the grid
1131    pub fn set_layout(&mut self, rows: i32, cols: i32) {
1132        unsafe { Fl_Grid_set_layout(self.inner.widget() as _, rows, cols, -1, -1) }
1133    }
1134    /// Layout the grid
1135    pub fn layout(&mut self) {
1136        unsafe { Fl_Grid_layout(self.inner.widget() as _) }
1137    }
1138    /// Clear the layout
1139    pub fn clear_layout(&mut self) {
1140        unsafe { Fl_Grid_clear_layout(self.inner.widget() as _) }
1141    }
1142    /// Set whether the Grid needs layout
1143    pub fn set_need_layout(&mut self, set: bool) {
1144        unsafe { Fl_Grid_set_need_layout(self.inner.widget() as _, set as _) }
1145    }
1146    /// Get whether the Grid needs layout
1147    pub fn need_layout(&self) -> bool {
1148        unsafe { Fl_Grid_need_layout(self.inner.widget() as _) != 0 }
1149    }
1150    /// Set the grid's margin
1151    pub fn set_margin(&mut self, left: i32, top: i32, right: i32, bottom: i32) {
1152        unsafe { Fl_Grid_set_margin(self.inner.widget() as _, left, top, right, bottom) }
1153    }
1154    /// Set the grid's margins
1155    pub fn set_margins(&mut self, left: i32, top: i32, right: i32, bottom: i32) {
1156        unsafe { Fl_Grid_set_margin(self.inner.widget() as _, left, top, right, bottom) }
1157    }
1158    /// Set the grid's gap
1159    pub fn set_gap(&mut self, row_gap: i32, col_gap: i32) {
1160        unsafe { Fl_Grid_set_gap(self.inner.widget() as _, row_gap, col_gap) }
1161    }
1162    #[allow(dead_code)]
1163    /// Set the widget at row/column and alignment
1164    pub fn set_widget_<W: WidgetExt>(
1165        &mut self,
1166        wi: &mut W,
1167        row: i32,
1168        col: i32,
1169        align: GridAlign,
1170    ) -> *mut () {
1171        unsafe {
1172            Fl_Grid_set_widget(
1173                self.inner.widget() as _,
1174                wi.as_widget_ptr() as _,
1175                row,
1176                col,
1177                align.bits(),
1178            ) as _
1179        }
1180    }
1181    /// Set the widget at row/column using ranges
1182    pub fn set_widget<W: 'static + Clone + WidgetExt>(
1183        &mut self,
1184        widget: &mut W,
1185        row: impl Into<GridRange>,
1186        col: impl Into<GridRange>,
1187    ) -> Result<(), FltkError> {
1188        let row = row.into();
1189        let col = col.into();
1190        self.set_widget_ext(widget, row, col, GridAlign::FILL)
1191    }
1192    /// Set the widget at row/column along with row span and column span and alignment
1193    fn set_widget_ext_<W: WidgetExt>(
1194        &mut self,
1195        wi: &mut W,
1196        row: i32,
1197        col: i32,
1198        rowspan: i32,
1199        colspan: i32,
1200        align: GridAlign,
1201    ) -> *mut () {
1202        unsafe {
1203            Fl_Grid_set_widget_ext(
1204                self.inner.widget() as _,
1205                wi.as_widget_ptr() as _,
1206                row,
1207                col,
1208                rowspan,
1209                colspan,
1210                align.bits(),
1211            ) as _
1212        }
1213    }
1214    /// Set the widget at row/column using ranges along with the alignment
1215    pub fn set_widget_ext<W: 'static + Clone + WidgetExt>(
1216        &mut self,
1217        widget: &mut W,
1218        row: impl Into<GridRange>,
1219        col: impl Into<GridRange>,
1220        align: GridAlign,
1221    ) -> Result<(), FltkError> {
1222        let row = row.into();
1223        let col = col.into();
1224        let e = self.set_widget_ext_(
1225            widget,
1226            row.start as _,
1227            col.start as _,
1228            row.len() as _,
1229            col.len() as _,
1230            align,
1231        );
1232        if e.is_null() {
1233            Err(FltkError::Internal(FltkErrorKind::FailedGridSetWidget))
1234        } else {
1235            Ok(())
1236        }
1237    }
1238    /// Set the column width
1239    pub fn set_col_width(&mut self, col: i32, value: i32) {
1240        unsafe { Fl_Grid_set_col_width(self.inner.widget() as _, col, value) }
1241    }
1242    /// Set the column weight
1243    pub fn set_col_weight(&mut self, col: i32, value: i32) {
1244        unsafe { Fl_Grid_set_col_weight(self.inner.widget() as _, col, value) }
1245    }
1246    /// Set the column gap
1247    pub fn set_col_gap(&mut self, col: i32, value: i32) {
1248        unsafe { Fl_Grid_set_col_gap(self.inner.widget() as _, col, value) }
1249    }
1250    /// Set the row height
1251    pub fn set_row_height(&mut self, row: i32, value: i32) {
1252        unsafe { Fl_Grid_set_row_height(self.inner.widget() as _, row, value) }
1253    }
1254    /// Set the row weight
1255    pub fn set_row_weight(&mut self, row: i32, value: i32) {
1256        unsafe { Fl_Grid_set_row_weight(self.inner.widget() as _, row, value) }
1257    }
1258    /// Set the row gap
1259    pub fn set_row_gap(&mut self, row: i32, value: i32) {
1260        unsafe { Fl_Grid_set_row_gap(self.inner.widget() as _, row, value) }
1261    }
1262    /// Show the grid
1263    pub fn show_grid(&mut self, set: bool) {
1264        unsafe { Fl_Grid_show_grid(self.inner.widget() as _, set as _) }
1265    }
1266    /// Show the grid with a certain color
1267    pub fn show_grid_with_color(&mut self, set: bool, col: Color) {
1268        unsafe { Fl_Grid_show_grid_with_color(self.inner.widget() as _, set as _, col.bits()) }
1269    }
1270    /// Debug the grid
1271    pub fn debug(&mut self, level: i32) {
1272        unsafe { Fl_Grid_debug(self.inner.widget() as _, level) }
1273    }
1274}
1275
1276#[allow(missing_docs)]
1277pub mod experimental {
1278    #[allow(unused_imports)]
1279    use super::*;
1280}