native_windows_gui/controls/
tabs.rs

1use winapi::shared::minwindef::{WPARAM, LPARAM, BOOL};
2use winapi::shared::windef::HWND;
3use winapi::um::winnt::LPWSTR;
4use winapi::um::winuser::{EnumChildWindows, WS_VISIBLE, WS_DISABLED, WS_EX_CONTROLPARENT};
5use crate::win32::{base_helper::{to_utf16, check_hwnd}, window_helper as wh};
6use crate::{NwgError, Font, RawEventHandler, unbind_raw_event_handler};
7use super::{ControlBase, ControlHandle};
8use std::{mem, cell::RefCell};
9
10#[cfg(feature="image-list")]
11use crate::ImageList;
12
13#[cfg(feature="image-list")]
14use std::ptr;
15
16const NOT_BOUND: &'static str = "TabsContainer/Tab is not yet bound to a winapi object";
17const BAD_HANDLE: &'static str = "INTERNAL ERROR: TabsContainer/Tab handle is not HWND!";
18
19
20bitflags! {
21    pub struct TabsContainerFlags: u32 {
22        const VISIBLE = WS_VISIBLE;
23        const DISABLED = WS_DISABLED;
24    }
25}
26
27/**
28A tabs container is a frame-like control that can contain `Tab` control.
29Tabs are added by specifying the `TabsContainer` as parent in the `Tab` builder.
30
31Do not add other control type as children to the TabsContainer
32
33Requires the `tabs` feature
34
35**Builder parameters:**
36  * `parent`:     **Required.** The button parent container.
37  * `position`:   The tab container position.
38  * `font`:       The font used for the tabs title
39  * `flags`:      A combination of the `TabsContainerFlags` values.
40  * `ex_flags`: A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
41  * `image_list`: The image list specifying the tabs icons
42
43
44**Control events:**
45  * `TabsContainerChanged`: The select tab of a TabsContainer changed
46  * `TabsContainerChanging`: The selected tab of a TabsContainer is about to be changed
47  * `MousePress(_)`: Generic mouse press events on the button
48  * `OnMouseMove`: Generic mouse mouse event
49  * `OnMouseWheel`: Generic mouse wheel event
50
51*/
52#[derive(Default)]
53pub struct TabsContainer {
54    pub handle: ControlHandle,
55    handler0: RefCell<Option<RawEventHandler>>,
56    handler1: RefCell<Option<RawEventHandler>>,
57}
58
59impl TabsContainer {
60
61    pub fn builder<'a>() -> TabsContainerBuilder<'a> {
62        TabsContainerBuilder {
63            size: (300, 300),
64            position: (0, 0),
65            parent: None,
66            font: None,
67            flags: None,
68            ex_flags: 0,
69
70            #[cfg(feature = "image-list")]
71            image_list: None
72        }
73    }
74
75    /// Return the index of the currently selected tab
76    /// May return `usize::max_value()` if no tab is selected
77    pub fn selected_tab(&self) -> usize {
78        use winapi::um::commctrl::{TCM_GETCURSEL};
79        
80        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
81        wh::send_message(handle, TCM_GETCURSEL, 0, 0) as usize
82    }
83
84    /// Set the currently selected tab by index
85    pub fn set_selected_tab(&self, index: usize) {
86        use winapi::um::commctrl::TCM_SETCURSEL;
87
88        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
89        wh::send_message(handle, TCM_SETCURSEL, index as WPARAM, 0);
90
91        // Update the visible state of the tabs (this is not done automatically)
92        let data: (HWND, i32) = (handle, index as i32);
93        let data_ptr = &data as *const (HWND, i32);
94        
95        unsafe {
96            EnumChildWindows(handle, Some(toggle_children_tabs), data_ptr as LPARAM);
97        }
98    }
99
100    /// Return the number of tabs in the view
101    pub fn tab_count(&self) -> usize {
102        use winapi::um::commctrl::TCM_GETITEMCOUNT;
103            
104        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
105        wh::send_message(handle, TCM_GETITEMCOUNT, 0, 0) as usize
106    }
107
108    /**
109        Sets the image list of the tab container. Pass None to remove the image list.
110
111        This is only available is the feature "image-list" is enabled.
112    */
113    #[cfg(feature = "image-list")]
114    pub fn set_image_list(&self, list: Option<&ImageList>) {
115        use winapi::um::commctrl::TCM_SETIMAGELIST;
116
117        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
118        let list_handle = match list {
119            None => 0,
120            Some(list) => list.handle as _
121        };
122
123        wh::send_message(handle, TCM_SETIMAGELIST, 0, list_handle);
124    }
125
126    /**
127        Returns a reference to the current image list in the tab container. The image list
128        is not owned and dropping it won't free the resources.
129
130        This is only available is the feature "image-list" is enabled.
131    */
132    #[cfg(feature = "image-list")]
133    pub fn image_list(&self) -> Option<ImageList> {
134        use winapi::um::commctrl::TCM_GETIMAGELIST;
135
136        let control_handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
137        let handle = wh::send_message(control_handle, TCM_GETIMAGELIST, 0, 0);
138        match handle == 0 {
139            true => None,
140            false => Some(ImageList {
141                handle: handle as _,
142                owned: false,
143            })
144        }
145    }
146
147    //
148    // Default methods
149    //
150
151    /// Return true if the control currently has the keyboard focus
152    pub fn focus(&self) -> bool {
153        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
154        unsafe { wh::get_focus(handle) }
155    }
156
157    /// Set the keyboard focus on the button.
158    pub fn set_focus(&self) {
159        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
160        unsafe { wh::set_focus(handle); }
161    }
162
163    /// Return true if the control user can interact with the control, return false otherwise
164    pub fn enabled(&self) -> bool {
165        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
166        unsafe { wh::get_window_enabled(handle) }
167    }
168
169    /// Enable or disable the control
170    pub fn set_enabled(&self, v: bool) {
171        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
172        unsafe { wh::set_window_enabled(handle, v) }
173    }
174
175    /// Return true if the control is visible to the user. Will return true even if the 
176    /// control is outside of the parent client view (ex: at the position (10000, 10000))
177    pub fn visible(&self) -> bool {
178        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
179        unsafe { wh::get_window_visibility(handle) }
180    }
181
182    /// Show or hide the control to the user
183    pub fn set_visible(&self, v: bool) {
184        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
185        unsafe { wh::set_window_visibility(handle, v) }
186    }
187
188    /// Return the size of the tabs container in the parent window
189    pub fn size(&self) -> (u32, u32) {
190        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
191        unsafe { wh::get_window_size(handle) }
192    }
193
194    /// Set the size of the tabs container in the parent window
195    pub fn set_size(&self, x: u32, y: u32) {
196        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
197        unsafe { wh::set_window_size(handle, x, y, false) }
198    }
199
200    /// Return the position of the tabs container in the parent window
201    pub fn position(&self) -> (i32, i32) {
202        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
203        unsafe { wh::get_window_position(handle) }
204    }
205
206    /// Set the position of the tabs container in the parent window
207    pub fn set_position(&self, x: i32, y: i32) {
208        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
209        unsafe { wh::set_window_position(handle, x, y) }
210    }
211
212    /// Return the font of the control
213    pub fn font(&self) -> Option<Font> {
214        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
215        let font_handle = wh::get_window_font(handle);
216        if font_handle.is_null() {
217            None
218        } else {
219            Some(Font { handle: font_handle })
220        }
221    }
222
223    /// Set the font of the control
224    pub fn set_font(&self, font: Option<&Font>) {
225        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
226        unsafe { wh::set_window_font(handle, font.map(|f| f.handle), true); }
227    }
228
229    /// Winapi class name used during control creation
230    pub fn class_name(&self) -> &'static str {
231        winapi::um::commctrl::WC_TABCONTROL
232    }
233
234    /// Winapi base flags used during window creation
235    pub fn flags(&self) -> u32 {
236        ::winapi::um::winuser::WS_VISIBLE
237    }
238
239    /// Winapi flags required by the control
240    pub fn forced_flags(&self) -> u32 {
241        use winapi::um::winuser::{WS_CHILD, WS_CLIPCHILDREN};
242        //use winapi::um::commctrl::TCS_OWNERDRAWFIXED;
243
244        WS_CHILD | WS_CLIPCHILDREN //| TCS_OWNERDRAWFIXED
245    }
246
247    //
248    // Private
249    //
250
251    /// The tab widget lacks basic functionalities on it's own. This fix it. 
252    fn hook_tabs(&self) {
253        use crate::bind_raw_event_handler_inner;
254        use winapi::shared::minwindef::{HIWORD, LOWORD};
255        use winapi::um::winuser::{NMHDR, WM_SIZE, WM_NOTIFY};
256        use winapi::um::commctrl::{TCM_GETCURSEL, TCN_SELCHANGE};
257        use winapi::um::winuser::SendMessageW;
258
259        if self.handle.blank() { panic!("{}", NOT_BOUND); }
260        let handle = self.handle.hwnd().expect(BAD_HANDLE);
261
262        let parent_handle_raw = wh::get_window_parent(handle);
263        let parent_handle = ControlHandle::Hwnd(parent_handle_raw);
264       
265        let handler0 = bind_raw_event_handler_inner(&parent_handle, handle as usize, move |_hwnd, msg, _w, l| { unsafe {
266            match msg {
267                WM_NOTIFY => {
268                    let nmhdr: &NMHDR = mem::transmute(l);
269                    if nmhdr.code == TCN_SELCHANGE {
270                        let index = SendMessageW(handle, TCM_GETCURSEL, 0, 0) as i32;
271                        let data: (HWND, i32) = (handle, index);
272                        let data_ptr = &data as *const (HWND, i32);
273                        EnumChildWindows(handle, Some(toggle_children_tabs), data_ptr as LPARAM);
274                    }
275                },
276                _ => {}
277            }
278
279            None
280        } });
281
282        let handler1 = bind_raw_event_handler_inner(&self.handle, handle as usize, move |hwnd, msg, _w, l| { unsafe {
283            match msg {
284                WM_SIZE => {
285                    use winapi::shared::windef::{RECT, HGDIOBJ};
286                    use winapi::um::winuser::{GetDC, DrawTextW, ReleaseDC, DT_CALCRECT, DT_LEFT};
287                    use winapi::um::wingdi::SelectObject;
288
289                    let size = l as u32;
290                    let width = LOWORD(size) as i32;
291                    let height = HIWORD(size) as i32;
292                    let (w, h) = crate::win32::high_dpi::physical_to_logical(width, height);
293
294                    let mut data = ResizeDirectChildrenParams {
295                        parent: hwnd,
296                        width: w as u32,
297                        height: h as u32,
298                        tab_offset_y: 0
299                    };
300
301                    // Get the height of the tabs
302                    let font_handle = wh::get_window_font(hwnd);
303                    let mut r: RECT = mem::zeroed();
304                    let dc = GetDC(hwnd);
305                    let old = SelectObject(dc, font_handle as HGDIOBJ);
306                    let calc: [u16;2] = [75, 121];
307                    DrawTextW(dc, calc.as_ptr(), 2, &mut r, DT_CALCRECT | DT_LEFT);
308                    SelectObject(dc, old);
309                    ReleaseDC(hwnd, dc);
310
311                    // Fix the width/height of the tabs
312                    const BORDER_SIZE: u32 = 11;
313                    let tab_height = r.bottom as u32 + BORDER_SIZE;
314                    if data.width > BORDER_SIZE { data.width -= BORDER_SIZE; }
315                    if data.height > tab_height { 
316                        data.height -= (tab_height + BORDER_SIZE).min(data.height);
317                    }
318                    data.tab_offset_y = tab_height;
319                    
320                    let data_ptr = &data as *const ResizeDirectChildrenParams;
321                    EnumChildWindows(hwnd, Some(resize_direct_children), mem::transmute(data_ptr));
322                },
323                _ => {}
324            }
325
326            None
327        } } );
328
329        *self.handler0.borrow_mut() = Some(handler0.unwrap());
330        *self.handler1.borrow_mut() = Some(handler1.unwrap());
331    }
332}
333
334impl Drop for TabsContainer {
335    fn drop(&mut self) {
336        let handler = self.handler0.borrow();
337        if let Some(h) = handler.as_ref() {
338            drop(unbind_raw_event_handler(h));
339        }
340
341        let handler = self.handler1.borrow();
342        if let Some(h) = handler.as_ref() {
343            drop(unbind_raw_event_handler(h));
344        }
345    
346        self.handle.destroy();
347    }
348}
349
350impl PartialEq for TabsContainer {
351    fn eq(&self, other: &Self) -> bool {
352        self.handle == other.handle
353    }
354}
355
356
357pub struct TabsContainerBuilder<'a> {
358    size: (i32, i32),
359    position: (i32, i32),
360    parent: Option<ControlHandle>,
361    font: Option<&'a Font>,
362    flags: Option<TabsContainerFlags>,
363    ex_flags: u32,
364
365    #[cfg(feature = "image-list")]
366    image_list: Option<&'a ImageList>
367}
368
369impl<'a> TabsContainerBuilder<'a> {
370
371    pub fn flags(mut self, flags: TabsContainerFlags) -> TabsContainerBuilder<'a> {
372        self.flags = Some(flags);
373        self
374    }
375
376    pub fn ex_flags(mut self, flags: u32) -> TabsContainerBuilder<'a> {
377        self.ex_flags = flags;
378        self
379    }
380
381    pub fn size(mut self, size: (i32, i32)) -> TabsContainerBuilder<'a> {
382        self.size = size;
383        self
384    }
385
386    pub fn position(mut self, pos: (i32, i32)) -> TabsContainerBuilder<'a> {
387        self.position = pos;
388        self
389    }
390
391    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> TabsContainerBuilder<'a> {
392        self.parent = Some(p.into());
393        self
394    }
395
396    pub fn font(mut self, font: Option<&'a Font>) -> TabsContainerBuilder<'a> {
397        self.font = font;
398        self
399    }
400
401    #[cfg(feature = "image-list")]
402    pub fn image_list(mut self, list: Option<&'a ImageList>) -> TabsContainerBuilder<'a> {
403        self.image_list = list;
404        self
405    }
406
407    pub fn build(self, out: &mut TabsContainer) -> Result<(), NwgError> {
408        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
409
410        let parent = match self.parent {
411            Some(p) => Ok(p),
412            None => Err(NwgError::no_parent("TabsContainer"))
413        }?;
414
415        *out = Default::default();
416
417        out.handle = ControlBase::build_hwnd()
418            .class_name(out.class_name())
419            .forced_flags(out.forced_flags())
420            .flags(flags)
421            .ex_flags(WS_EX_CONTROLPARENT | self.ex_flags)
422            .size(self.size)
423            .position(self.position)
424            .parent(Some(parent))
425            .build()?;
426
427        out.hook_tabs();
428
429        if self.font.is_some() {
430            out.set_font(self.font);
431        } else {
432            out.set_font(Font::global_default().as_ref());
433        }
434
435        // Image list
436        #[cfg(feature = "image-list")]
437        fn set_image_list(b: &TabsContainerBuilder, out: &mut TabsContainer) {
438            if b.image_list.is_some() {
439                out.set_image_list(b.image_list);
440            }
441        }
442
443        #[cfg(not(feature = "image-list"))]
444        fn set_image_list(_b: &TabsContainerBuilder, _out: &mut TabsContainer) {}
445
446        set_image_list(&self, out);
447
448        Ok(())
449    }
450}
451
452
453/**
454A subwindow in a `TabContainer` widget. A Tab control can only be added as a child of a `TabContainer`. 
455
456A Tab controls doesn't do much on its own. See `TabContainer` for the tab specific events.
457
458**Builder parameters:**
459  * `parent`:      **Required.** The Tab parent container.
460  * `text`:        The tab text
461  * `image_index`: The tab icon index in the tab container image list
462*/
463#[derive(Default, Debug, PartialEq, Eq)]
464pub struct Tab {
465    pub handle: ControlHandle
466}
467
468impl Tab {
469
470    pub fn builder<'a>() -> TabBuilder<'a> {
471        TabBuilder {
472            text: "Tab",
473            parent: None,
474
475            #[cfg(feature = "image-list")]
476            image_index: None,
477        }
478    }
479
480    /// Sets the title of the tab
481    pub fn set_text<'a>(&self, text: &'a str) {
482        use winapi::um::commctrl::{TCM_SETITEMW, TCIF_TEXT, TCITEMW};
483        use winapi::um::winuser::GWL_USERDATA;
484
485        if self.handle.blank() { panic!("{}", NOT_BOUND); }
486        let handle = self.handle.hwnd().expect(BAD_HANDLE);
487
488        let tab_index = (wh::get_window_long(handle, GWL_USERDATA) - 1) as WPARAM;
489
490        let tab_view_handle = wh::get_window_parent(handle);
491
492        let text = to_utf16(text);
493        let item = TCITEMW {
494            mask: TCIF_TEXT,
495            dwState: 0,
496            dwStateMask: 0,
497            pszText: text.as_ptr() as LPWSTR,
498            cchTextMax: 0,
499            iImage: -1,
500            lParam: 0
501        };
502
503        let item_ptr = &item as *const TCITEMW;
504        wh::send_message(tab_view_handle, TCM_SETITEMW, tab_index, item_ptr as LPARAM);
505    }
506
507    /**
508        Sets the image of the tab. index is the index of the image in the tab container image list.
509
510        This is only available if the "image-list" feature is enabled
511    */
512    #[cfg(feature = "image-list")]
513    pub fn set_image_index(&self, index: Option<i32>) {
514        use winapi::um::commctrl::{TCM_SETITEMW, TCIF_IMAGE, TCITEMW};
515        use winapi::um::winuser::GWL_USERDATA;
516
517        if self.handle.blank() { panic!("{}", NOT_BOUND); }
518        let handle = self.handle.hwnd().expect(BAD_HANDLE);
519
520        let tab_index = (wh::get_window_long(handle, GWL_USERDATA) - 1) as WPARAM;
521        let tab_view_handle = wh::get_window_parent(handle);
522
523        let item = TCITEMW {
524            mask: TCIF_IMAGE,
525            dwState: 0,
526            dwStateMask: 0,
527            pszText: ptr::null_mut(),
528            cchTextMax: 0,
529            iImage: index.unwrap_or(-1),
530            lParam: 0
531        };
532
533        let item_ptr = &item as *const TCITEMW;
534        wh::send_message(tab_view_handle, TCM_SETITEMW, tab_index, item_ptr as LPARAM);
535    }
536
537    /**
538        Returns the index of image of the tab.
539        The index maps to the image list of the tab container.
540
541        This is only available if the "image-list" feature is enabled
542    */
543    #[cfg(feature = "image-list")]
544    pub fn image_index(&self) -> Option<i32> {
545        None
546    }
547
548    /// Returns true if the control is visible to the user. Will return true even if the 
549    /// control is outside of the parent client view (ex: at the position (10000, 10000))
550    pub fn visible(&self) -> bool {
551        if self.handle.blank() { panic!("{}", NOT_BOUND); }
552        let handle = self.handle.hwnd().expect(BAD_HANDLE);
553        unsafe { wh::get_window_visibility(handle) }
554    }
555
556    /// Show or hide the control to the user
557    pub fn set_visible(&self, v: bool) {
558        if self.handle.blank() { panic!("{}", NOT_BOUND); }
559        let handle = self.handle.hwnd().expect(BAD_HANDLE);
560        unsafe { wh::set_window_visibility(handle, v) }
561    }
562    
563
564    //
565    // Other methods
566    //
567
568    /// Winapi class name used during control creation
569    pub fn class_name(&self) -> &'static str {
570        "NWG_TAB"
571    }
572
573    /// Winapi base flags used during window creation
574    pub fn flags(&self) -> u32 {
575        0
576    }
577
578    /// Winapi flags required by the control
579    pub fn forced_flags(&self) -> u32 {
580        //use winapi::um::commctrl::{TCS_SINGLELINE};
581        use winapi::um::winuser::{WS_CHILD, WS_CLIPCHILDREN};
582
583        WS_CHILD | WS_CLIPCHILDREN
584    }
585
586    /// Set and initialize a tab as active
587    unsafe fn init(current_handle: HWND, tab_view_handle: HWND, index: usize) {
588        use winapi::um::winuser::GWL_USERDATA;
589
590        // Save the index of the tab in the window data
591        wh::set_window_long(current_handle, GWL_USERDATA, index);
592
593        // Resize the tabs so that they match the tab view size and hide all children tabs
594        let (w, h) = wh::get_window_size(tab_view_handle);
595        let width = w - 11;
596        let height = h - 33;
597
598        // Resize the tab to match the tab view
599        wh::set_window_size(current_handle, width, height, false);
600
601        // Move the tab under the headers
602        wh::set_window_position(current_handle, 5, 25);
603
604        // Make the current tab visible
605        if index == 1 {
606            wh::set_window_visibility(current_handle, true);
607        }
608    }
609
610    fn next_index(tab_view_handle: HWND) -> usize {
611        let mut count = 0;
612        let count_ptr = &mut count as *mut usize;
613
614        unsafe {
615            EnumChildWindows(tab_view_handle, Some(count_children), mem::transmute(count_ptr));
616        }
617
618        count
619    }
620
621    /// Bind the tab to a tab view
622    fn bind_container<'a>(&self, text: &'a str) {
623        use winapi::um::commctrl::{TCITEMW, TCM_INSERTITEMW, TCIF_TEXT};
624
625        if self.handle.blank() { panic!("{}", NOT_BOUND); }
626        let handle = self.handle.hwnd().expect(BAD_HANDLE);
627
628        let tab_view_handle = wh::get_window_parent(handle);
629        let next_index = Tab::next_index(tab_view_handle);
630
631        unsafe {
632            Tab::init(handle, tab_view_handle, next_index);
633        }
634
635        let text = to_utf16(&text);
636        let tab_info = TCITEMW {
637            mask: TCIF_TEXT,
638            dwState: 0,
639            dwStateMask: 0,
640            pszText: text.as_ptr() as LPWSTR,
641            cchTextMax: 0,
642            iImage: -1,
643            lParam: 0
644        };
645
646        let tab_info_ptr = &tab_info as *const TCITEMW;
647        wh::send_message(tab_view_handle, TCM_INSERTITEMW, next_index as WPARAM, tab_info_ptr as LPARAM);
648    }
649
650}
651
652impl Drop for Tab {
653    fn drop(&mut self) {
654        self.handle.destroy();
655    }
656}
657
658pub struct TabBuilder<'a> {
659    text: &'a str,
660    parent: Option<ControlHandle>,
661
662    #[cfg(feature = "image-list")]
663    image_index: Option<i32>,
664}
665
666impl<'a> TabBuilder<'a> {
667
668    pub fn text(mut self, text: &'a str) -> TabBuilder<'a> {
669        self.text = text;
670        self
671    }
672
673    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> TabBuilder<'a> {
674        self.parent = Some(p.into());
675        self
676    }
677
678    #[cfg(feature = "image-list")]
679    pub fn image_index(mut self, index: Option<i32>) -> TabBuilder<'a> {
680        self.image_index = index;
681        self
682    }
683
684    pub fn build(self, out: &mut Tab) -> Result<(), NwgError> {
685        use winapi::um::commctrl::WC_TABCONTROL;
686
687        let parent = match self.parent {
688            Some(p) => Ok(p),
689            None => Err(NwgError::no_parent("Tab"))
690        }?;
691
692        *out = Default::default();
693
694        match &parent {
695            &ControlHandle::Hwnd(h) => {
696                let class_name = unsafe { wh::get_window_class_name(h) };
697                if &class_name != WC_TABCONTROL {
698                    Err(NwgError::control_create("Tab requires a TabsContainer parent."))
699                } else {
700                    Ok(())
701                }
702            },
703            _ => Err(NwgError::control_create("Tab requires a TabsContainer parent."))
704        }?;
705
706        out.handle = ControlBase::build_hwnd()
707            .class_name(out.class_name())
708            .forced_flags(out.forced_flags())
709            .ex_flags(WS_EX_CONTROLPARENT)
710            .flags(out.flags())
711            .text(self.text)
712            .parent(Some(parent))
713            .build()?;
714
715        out.bind_container(self.text);
716
717        // Image index
718
719        #[cfg(feature = "image-list")]
720        fn set_image_index<'a>(b: &TabBuilder<'a>, out: &mut Tab) {
721            if b.image_index.is_some() {
722                out.set_image_index(b.image_index);
723            }
724        }
725
726        #[cfg(not(feature = "image-list"))]
727        fn set_image_index<'a>(_b: &TabBuilder<'a>, _out: &mut Tab) {}
728
729        set_image_index(&self, out);
730
731        Ok(())
732    }
733}
734
735
736struct ResizeDirectChildrenParams {
737    parent: HWND,
738    width: u32,
739    height: u32,
740    tab_offset_y: u32
741}
742
743unsafe extern "system" fn resize_direct_children(handle: HWND, params: LPARAM) -> BOOL {
744    let params: &ResizeDirectChildrenParams = &*(params as *const ResizeDirectChildrenParams);
745    if wh::get_window_parent(handle) == params.parent {
746        wh::set_window_size(handle, params.width, params.height, false);
747
748        let (x, _y) = wh::get_window_position(handle);
749        wh::set_window_position(handle, x, params.tab_offset_y as i32);
750    }
751
752    1
753}
754
755unsafe extern "system" fn count_children(handle: HWND, params: LPARAM) -> BOOL {
756    use winapi::um::winuser::GWL_USERDATA;
757
758    if &wh::get_window_class_name(handle) == "NWG_TAB" {
759        let tab_index = (wh::get_window_long(handle, GWL_USERDATA)) as WPARAM;
760        let count: &mut usize = ::std::mem::transmute(params);
761        *count = usize::max(tab_index+1, *count);
762    }
763    
764    1
765}
766
767/// Toggle the visibility of the active and inactive tab.
768unsafe extern "system" fn toggle_children_tabs(handle: HWND, params: LPARAM) -> BOOL {
769    use winapi::um::winuser::GWL_USERDATA;
770    
771    let &(parent, index): &(HWND, i32) = mem::transmute(params);
772    if wh::get_window_parent(handle) == parent {
773        let tab_index = wh::get_window_long(handle, GWL_USERDATA) as i32;
774        let visible = tab_index == index + 1;
775        wh::set_window_visibility(handle, visible);
776    }
777
778    1
779}