Skip to main content

vb6parse/language/controls/
mod.rs

1//! VB6 Control definitions and properties.
2//!
3//! This module contains the definitions for various VB6 controls,
4//! including their properties and enumerations used to represent
5//! different settings for these controls.
6//! Each control is represented as a struct with associated properties,
7//! and enumerations are used to define specific options for properties
8//! such as alignment, visibility, and behavior.
9//! This module is essential for parsing and representing VB6 forms
10//! and their controls in a structured manner.
11//!
12//! References to official Microsoft documentation are provided for
13//! each property and enumeration to ensure accuracy and completeness.
14//!
15//! Modules for individual controls are also included, each defining
16//! the properties specific to that control type.
17//!
18
19pub mod checkbox;
20pub mod combobox;
21pub mod commandbutton;
22pub mod custom;
23pub mod data;
24pub mod dirlistbox;
25pub mod drivelistbox;
26pub mod filelistbox;
27pub mod form;
28pub mod form_root;
29pub mod frame;
30pub mod image;
31pub mod label;
32pub mod line;
33pub mod listbox;
34pub mod mdiform;
35pub mod menus;
36pub mod ole;
37pub mod optionbutton;
38pub mod picturebox;
39pub mod scrollbars;
40pub mod shape;
41pub mod textbox;
42pub mod timer;
43
44use std::convert::{From, TryFrom};
45use std::fmt::{Display, Formatter};
46use std::str::FromStr;
47
48use num_enum::TryFromPrimitive;
49use serde::Serialize;
50
51use crate::errors::{ErrorKind, FormError};
52use crate::language::PropertyGroup;
53
54use crate::language::controls::{
55    checkbox::CheckBoxProperties,
56    combobox::ComboBoxProperties,
57    commandbutton::CommandButtonProperties,
58    custom::CustomControlProperties,
59    data::DataProperties,
60    dirlistbox::DirListBoxProperties,
61    drivelistbox::DriveListBoxProperties,
62    filelistbox::FileListBoxProperties,
63    frame::FrameProperties,
64    image::ImageProperties,
65    label::LabelProperties,
66    line::LineProperties,
67    listbox::ListBoxProperties,
68    menus::{MenuControl, MenuProperties},
69    ole::OLEProperties,
70    optionbutton::OptionButtonProperties,
71    picturebox::PictureBoxProperties,
72    scrollbars::ScrollBarProperties,
73    shape::ShapeProperties,
74    textbox::TextBoxProperties,
75    timer::TimerProperties,
76};
77
78// Re-export form root types at the controls module level
79pub use form_root::{Form, FormRoot, MDIForm};
80
81/// The `Font` defines the characteristics of the text used by a control or form.
82///
83/// [Reference](https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/font-object-microsoft-forms)
84#[derive(Debug, PartialEq, Clone, Serialize, PartialOrd)]
85pub struct Font {
86    /// The name of the font.
87    ///
88    /// Default value is "MS Sans Serif".
89    pub name: String,
90    /// The Size property determines the height, in points, of displayed text.
91    ///
92    /// Default value is 8.0
93    pub size: f32,
94    /// Which character set to use. 0 is English and the only one currently supported.
95    ///
96    /// Default value is 0
97    pub charset: i32,
98    /// The Weight property determines the darkness of the text.
99    ///
100    /// Default value is 400
101    pub weight: i32,
102    /// Should the text be underlined.
103    ///
104    /// Default value is false
105    pub underline: bool,
106    /// Should the text be italic.
107    ///
108    /// Default value is false
109    pub italic: bool,
110    /// Should the text have a strikethrough.
111    ///
112    /// Default value is false
113    pub strikethrough: bool,
114}
115
116impl Default for Font {
117    fn default() -> Self {
118        Font {
119            name: "MS Sans Serif".to_string(),
120            size: 8.0,
121            charset: 0,
122            weight: 400,
123            underline: false,
124            italic: false,
125            strikethrough: false,
126        }
127    }
128}
129
130/// `AutoRedraw` determines if the control is redrawn automatically when something is
131/// moved in front of it or if it is redrawn manually.
132///
133/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa245029(v=vs.60))
134#[derive(
135    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
136)]
137#[repr(i32)]
138pub enum AutoRedraw {
139    /// Disables automatic repainting of an object and writes graphics or text
140    /// only to the screen. Visual Basic invokes the object's `Paint` event when
141    /// necessary to repaint the object.
142    ///
143    /// This is the default setting.
144    #[default]
145    Manual = 0,
146    /// Enables automatic repainting of a `Form` object or `PictureBox` control.
147    /// Graphics and text are written to the screen and to an image stored in memory.
148    /// The object doesn't receive `Paint` events; it's repainted when necessary,
149    /// using the image stored in memory.
150    Automatic = -1,
151}
152
153impl TryFrom<&str> for AutoRedraw {
154    type Error = ErrorKind;
155
156    fn try_from(value: &str) -> Result<Self, Self::Error> {
157        match value {
158            "0" => Ok(AutoRedraw::Manual),
159            "-1" => Ok(AutoRedraw::Automatic),
160            _ => Err(ErrorKind::Form(FormError::InvalidAutoRedraw {
161                value: value.to_string(),
162            })),
163        }
164    }
165}
166
167impl FromStr for AutoRedraw {
168    type Err = ErrorKind;
169
170    fn from_str(s: &str) -> Result<Self, Self::Err> {
171        AutoRedraw::try_from(s)
172    }
173}
174
175impl From<bool> for AutoRedraw {
176    fn from(value: bool) -> Self {
177        if value {
178            AutoRedraw::Automatic
179        } else {
180            AutoRedraw::Manual
181        }
182    }
183}
184
185impl Display for AutoRedraw {
186    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
187        let text = match self {
188            AutoRedraw::Manual => "Manual",
189            AutoRedraw::Automatic => "Automatic",
190        };
191        write!(f, "{text}")
192    }
193}
194
195/// `TextDirection` determines the direction in which text is displayed in the control.
196///
197/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa442921(v=vs.60))
198#[derive(
199    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
200)]
201#[repr(i32)]
202pub enum TextDirection {
203    /// The text is ordered from left to right.
204    ///
205    /// This is the default setting.
206    #[default]
207    LeftToRight = 0,
208    /// The text is ordered from right to left.
209    RightToLeft = -1,
210}
211
212impl TryFrom<&str> for TextDirection {
213    type Error = ErrorKind;
214
215    fn try_from(value: &str) -> Result<Self, Self::Error> {
216        match value {
217            "0" => Ok(TextDirection::LeftToRight),
218            "-1" => Ok(TextDirection::RightToLeft),
219            _ => Err(ErrorKind::Form(FormError::InvalidTextDirection {
220                value: value.to_string(),
221            })),
222        }
223    }
224}
225
226impl FromStr for TextDirection {
227    type Err = ErrorKind;
228
229    fn from_str(s: &str) -> Result<Self, ErrorKind> {
230        TextDirection::try_from(s)
231    }
232}
233
234impl From<bool> for TextDirection {
235    fn from(value: bool) -> Self {
236        if value {
237            TextDirection::RightToLeft
238        } else {
239            TextDirection::LeftToRight
240        }
241    }
242}
243
244impl Display for TextDirection {
245    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
246        let text = match self {
247            TextDirection::LeftToRight => "Left to Right",
248            TextDirection::RightToLeft => "Right to Left",
249        };
250        write!(f, "{text}")
251    }
252}
253
254/// `AutoSize` determines if the control is automatically resized to fit its contents.
255/// This is used with the `Label` control and the `PictureBox` control.
256///
257/// In a `PictureBox`, this property is used to determine if the control is automatically resized
258/// to fit the size of the picture. If set to `Fixed` the control is not resized and the picture
259/// will be scaled or clipped depending on other properties like `SizeMode`.
260///
261/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa245034(v=vs.60))
262#[derive(
263    Debug,
264    PartialEq,
265    Eq,
266    Clone,
267    serde::Serialize,
268    Default,
269    TryFromPrimitive,
270    Copy,
271    Hash,
272    PartialOrd,
273    Ord,
274)]
275#[repr(i32)]
276pub enum AutoSize {
277    /// Keeps the size of the control constant. Contents are clipped when they
278    /// exceed the area of the control.
279    ///
280    /// This is the default setting.
281    #[default]
282    Fixed = 0,
283    /// Automatically resizes the control to display its entire contents.
284    Resize = -1,
285}
286
287impl TryFrom<&str> for AutoSize {
288    type Error = ErrorKind;
289
290    fn try_from(value: &str) -> Result<Self, ErrorKind> {
291        match value {
292            "0" => Ok(AutoSize::Fixed),
293            "-1" => Ok(AutoSize::Resize),
294            _ => Err(ErrorKind::Form(FormError::InvalidAutoSize {
295                value: value.to_string(),
296            })),
297        }
298    }
299}
300
301impl From<bool> for AutoSize {
302    fn from(value: bool) -> Self {
303        if value {
304            AutoSize::Resize
305        } else {
306            AutoSize::Fixed
307        }
308    }
309}
310
311impl FromStr for AutoSize {
312    type Err = ErrorKind;
313
314    fn from_str(s: &str) -> Result<Self, ErrorKind> {
315        AutoSize::try_from(s)
316    }
317}
318
319impl Display for AutoSize {
320    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
321        let text = match self {
322            AutoSize::Fixed => "Fixed",
323            AutoSize::Resize => "Resize",
324        };
325        write!(f, "{text}")
326    }
327}
328
329/// Determines if a control or form can respond to user-generated events.
330///
331/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa267301(v=vs.60))
332#[derive(
333    Debug,
334    PartialEq,
335    Eq,
336    Clone,
337    serde::Serialize,
338    Default,
339    TryFromPrimitive,
340    Copy,
341    Hash,
342    PartialOrd,
343    Ord,
344)]
345#[repr(i32)]
346pub enum Activation {
347    /// The control is disabled and will not respond to user-generated events.
348    Disabled = 0,
349    /// The control is enabled and will respond to user-generated events.
350    ///
351    /// This is the default setting.
352    #[default]
353    Enabled = -1,
354}
355
356impl From<bool> for Activation {
357    fn from(value: bool) -> Self {
358        if value {
359            Activation::Enabled
360        } else {
361            Activation::Disabled
362        }
363    }
364}
365
366impl TryFrom<&str> for Activation {
367    type Error = ErrorKind;
368
369    fn try_from(value: &str) -> Result<Self, Self::Error> {
370        match value {
371            "0" => Ok(Activation::Disabled),
372            "-1" => Ok(Activation::Enabled),
373            _ => Err(ErrorKind::Form(FormError::InvalidActivation {
374                value: value.to_string(),
375            })),
376        }
377    }
378}
379
380impl Display for Activation {
381    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
382        let text = match self {
383            Activation::Disabled => "Disabled",
384            Activation::Enabled => "Enabled",
385        };
386        write!(f, "{text}")
387    }
388}
389
390/// `TabStop` determines if the control is included in the tab order.
391/// In VB6, the `TabStop` property determines whether a control can receive focus
392/// when the user navigates through controls using the Tab key.
393///
394/// When `TabStop` is set to `Included`, the control is included in the tab order
395/// and can receive focus when the user presses the Tab key.
396///
397/// When `TabStop` is set to `ProgrammaticOnly`, the control is skipped in the
398/// tab order and cannot receive focus via the Tab key.
399/// However, it can still receive focus programmatically or through other user interactions.
400///
401/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa445721(v=vs.60))
402#[derive(
403    Debug,
404    PartialEq,
405    Eq,
406    Clone,
407    serde::Serialize,
408    Default,
409    TryFromPrimitive,
410    Copy,
411    Hash,
412    PartialOrd,
413    Ord,
414)]
415#[repr(i32)]
416pub enum TabStop {
417    /// Bypasses the object when the user is tabbing, although the object still
418    /// holds its place in the actual tab order, as determined by the `TabIndex`
419    /// property.
420    ProgrammaticOnly = 0,
421    /// Designates the object as a tab stop.
422    ///
423    /// This is the default setting.
424    #[default]
425    Included = -1,
426}
427
428impl From<bool> for TabStop {
429    fn from(value: bool) -> Self {
430        if value {
431            TabStop::Included
432        } else {
433            TabStop::ProgrammaticOnly
434        }
435    }
436}
437
438impl TryFrom<&str> for TabStop {
439    type Error = ErrorKind;
440
441    fn try_from(value: &str) -> Result<Self, Self::Error> {
442        match value {
443            "0" => Ok(TabStop::ProgrammaticOnly),
444            "-1" => Ok(TabStop::Included),
445            _ => Err(ErrorKind::Form(FormError::InvalidTabStop {
446                value: value.to_string(),
447            })),
448        }
449    }
450}
451
452impl Display for TabStop {
453    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
454        let text = match self {
455            TabStop::ProgrammaticOnly => "Programmatic Only",
456            TabStop::Included => "Included",
457        };
458        write!(f, "{text}")
459    }
460}
461
462/// Determines if the control is visible or hidden.
463///
464/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa445768(v=vs.60))
465#[derive(
466    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
467)]
468#[repr(i32)]
469pub enum Visibility {
470    /// The control is not visible.
471    Hidden = 0,
472    /// The control is visible.
473    ///
474    /// This is the default setting.
475    #[default]
476    Visible = -1,
477}
478
479impl TryFrom<&str> for Visibility {
480    type Error = ErrorKind;
481
482    fn try_from(value: &str) -> Result<Self, Self::Error> {
483        match value {
484            "0" => Ok(Visibility::Hidden),
485            "-1" => Ok(Visibility::Visible),
486            _ => Err(ErrorKind::Form(FormError::InvalidVisibility {
487                value: value.to_string(),
488            })),
489        }
490    }
491}
492
493impl FromStr for Visibility {
494    type Err = ErrorKind;
495
496    fn from_str(s: &str) -> Result<Self, Self::Err> {
497        Visibility::try_from(s)
498    }
499}
500
501impl From<bool> for Visibility {
502    fn from(value: bool) -> Self {
503        if value {
504            Visibility::Visible
505        } else {
506            Visibility::Hidden
507        }
508    }
509}
510
511impl Display for Visibility {
512    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
513        let text = match self {
514            Visibility::Hidden => "Hidden",
515            Visibility::Visible => "Visible",
516        };
517        write!(f, "{text}")
518    }
519}
520
521/// Determines if the control has a device context.
522///
523/// A device context is a Windows data structure that defines a set of graphic
524/// objects and their associated attributes, and it defines a mapping between
525/// the logical coordinates and device coordinates for a particular device, such
526/// as a display or printer.
527///
528/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa245860(v=vs.60))
529#[derive(
530    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
531)]
532#[repr(i32)]
533pub enum HasDeviceContext {
534    /// The control does not have a device context.
535    NoContext = 0,
536    /// The control has a device context.
537    ///
538    /// This is the default setting.
539    #[default]
540    HasContext = -1,
541}
542
543impl TryFrom<&str> for HasDeviceContext {
544    type Error = ErrorKind;
545
546    fn try_from(value: &str) -> Result<Self, Self::Error> {
547        match value {
548            "0" => Ok(HasDeviceContext::NoContext),
549            "-1" => Ok(HasDeviceContext::HasContext),
550            _ => Err(ErrorKind::Form(FormError::InvalidHasDeviceContext {
551                value: value.to_string(),
552            })),
553        }
554    }
555}
556
557impl FromStr for HasDeviceContext {
558    type Err = ErrorKind;
559
560    fn from_str(s: &str) -> Result<Self, Self::Err> {
561        HasDeviceContext::try_from(s)
562    }
563}
564
565impl From<bool> for HasDeviceContext {
566    fn from(value: bool) -> Self {
567        if value {
568            HasDeviceContext::HasContext
569        } else {
570            HasDeviceContext::NoContext
571        }
572    }
573}
574
575impl Display for HasDeviceContext {
576    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
577        let text = match self {
578            HasDeviceContext::NoContext => "No Context",
579            HasDeviceContext::HasContext => "Has Context",
580        };
581        write!(f, "{text}")
582    }
583}
584
585/// Determines whether the color assigned in the `mask_color` property is used
586/// as a mask.
587/// That is, if it is used to create transparent regions.
588///
589/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa445753(v=vs.60))
590#[derive(
591    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
592)]
593#[repr(i32)]
594pub enum UseMaskColor {
595    /// The control does not use the mask color.
596    ///
597    /// This is the default setting.
598    #[default]
599    DoNotUseMaskColor = 0,
600    /// The color assigned to the `mask_color` property is used as a mask,
601    /// creating a transparent region wherever that color is.
602    UseMaskColor = -1,
603}
604
605impl Display for UseMaskColor {
606    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
607        let text = match self {
608            UseMaskColor::DoNotUseMaskColor => "Do not use Mask Color",
609            UseMaskColor::UseMaskColor => "Use Mask Color",
610        };
611        write!(f, "{text}")
612    }
613}
614
615/// Determines if the control causes validation.
616/// In VB6, the `CausesValidation` property determines whether a control causes validation
617/// to occur when the user attempts to move focus from the control.
618///
619/// If `CausesValidation` is set to `true`, validation occurs when the user attempts to move
620/// focus from the control to another control.
621///
622/// If `CausesValidation` is set to `false`, validation does not occur when the user attempts
623/// to move focus from the control to another control.
624///
625/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa245065(v=vs.60))
626#[derive(
627    Debug,
628    PartialEq,
629    Eq,
630    Clone,
631    serde::Serialize,
632    Default,
633    TryFromPrimitive,
634    Copy,
635    Hash,
636    PartialOrd,
637    Ord,
638)]
639#[repr(i32)]
640pub enum CausesValidation {
641    /// The control does not cause validation.
642    ///
643    /// The control from which the focus has shifted does not fire its `Validate` event.
644    No = 0,
645    /// The control causes validation.
646    /// The control from which the focus has shifted fires its `Validate` event.
647    ///
648    /// This is the default setting.
649    #[default]
650    Yes = -1,
651}
652
653impl TryFrom<&str> for CausesValidation {
654    type Error = ErrorKind;
655
656    fn try_from(value: &str) -> Result<Self, Self::Error> {
657        match value {
658            "0" => Ok(CausesValidation::No),
659            "-1" => Ok(CausesValidation::Yes),
660            _ => Err(ErrorKind::Form(FormError::InvalidCausesValidation {
661                value: value.to_string(),
662            })),
663        }
664    }
665}
666
667impl FromStr for CausesValidation {
668    type Err = ErrorKind;
669
670    fn from_str(s: &str) -> Result<Self, Self::Err> {
671        CausesValidation::try_from(s)
672    }
673}
674
675impl From<bool> for CausesValidation {
676    fn from(value: bool) -> Self {
677        if value {
678            CausesValidation::Yes
679        } else {
680            CausesValidation::No
681        }
682    }
683}
684
685impl Display for CausesValidation {
686    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
687        let text = match self {
688            CausesValidation::No => "No",
689            CausesValidation::Yes => "Yes",
690        };
691        write!(f, "{text}")
692    }
693}
694
695/// The `Movability` property of a `Form` control determines whether the
696/// form can be moved by the user. If the form is not moveable, the user cannot
697/// move the form by dragging its title bar or by using the arrow keys.
698/// If the form is moveable, the user can move the form by dragging its title
699/// bar or by using the arrow keys.
700///
701/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa235194(v=vs.60))
702#[derive(
703    Debug,
704    PartialEq,
705    Eq,
706    Clone,
707    Default,
708    TryFromPrimitive,
709    serde::Serialize,
710    Copy,
711    Hash,
712    PartialOrd,
713    Ord,
714)]
715#[repr(i32)]
716pub enum Movability {
717    /// The form is not moveable.
718    Fixed = 0,
719    /// The form is moveable.
720    ///
721    /// This is the default setting.
722    #[default]
723    Moveable = -1,
724}
725
726impl TryFrom<&str> for Movability {
727    type Error = ErrorKind;
728
729    fn try_from(value: &str) -> Result<Self, Self::Error> {
730        match value {
731            "0" => Ok(Movability::Fixed),
732            "-1" => Ok(Movability::Moveable),
733            _ => Err(ErrorKind::Form(FormError::InvalidMovability {
734                value: value.to_string(),
735            })),
736        }
737    }
738}
739
740impl FromStr for Movability {
741    type Err = ErrorKind;
742
743    fn from_str(s: &str) -> Result<Self, Self::Err> {
744        Movability::try_from(s)
745    }
746}
747
748impl From<bool> for Movability {
749    fn from(value: bool) -> Self {
750        if value {
751            Movability::Moveable
752        } else {
753            Movability::Fixed
754        }
755    }
756}
757
758impl Display for Movability {
759    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
760        let text = match self {
761            Movability::Fixed => "Fixed",
762            Movability::Moveable => "Moveable",
763        };
764        write!(f, "{text}")
765    }
766}
767
768/// Determines whether background text and graphics on a `Form` or a `PictureBox`
769/// control are displayed in the spaces around characters.
770///
771/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa267490(v=vs.60))
772#[derive(
773    Debug,
774    PartialEq,
775    Eq,
776    Clone,
777    Default,
778    TryFromPrimitive,
779    serde::Serialize,
780    Copy,
781    Hash,
782    PartialOrd,
783    Ord,
784)]
785#[repr(i32)]
786pub enum FontTransparency {
787    /// Masks existing background graphics and text around the characters of a
788    /// font.
789    Opaque = 0,
790    /// Permits background graphics and text to show around the spaces of the
791    /// characters in a font.
792    ///
793    /// This is the default setting.
794    #[default]
795    Transparent = -1,
796}
797
798impl TryFrom<&str> for FontTransparency {
799    type Error = ErrorKind;
800
801    fn try_from(value: &str) -> Result<Self, Self::Error> {
802        match value {
803            "0" => Ok(FontTransparency::Opaque),
804            "-1" => Ok(FontTransparency::Transparent),
805            _ => Err(ErrorKind::Form(FormError::InvalidFontTransparency {
806                value: value.to_string(),
807            })),
808        }
809    }
810}
811
812impl FromStr for FontTransparency {
813    type Err = ErrorKind;
814
815    fn from_str(s: &str) -> Result<Self, Self::Err> {
816        FontTransparency::try_from(s)
817    }
818}
819
820impl From<bool> for FontTransparency {
821    fn from(value: bool) -> Self {
822        if value {
823            FontTransparency::Transparent
824        } else {
825            FontTransparency::Opaque
826        }
827    }
828}
829
830impl Display for FontTransparency {
831    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
832        let text = match self {
833            FontTransparency::Opaque => "Opaque",
834            FontTransparency::Transparent => "Transparent",
835        };
836        write!(f, "{text}")
837    }
838}
839
840/// Determines whether context-sensitive Help uses the What's This pop-up
841/// (provided by Help in 32-bit Windows operating systems) or the main Help
842/// window.
843///
844/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa445772(v=vs.60))
845#[derive(
846    Debug,
847    PartialEq,
848    Eq,
849    Clone,
850    Default,
851    TryFromPrimitive,
852    serde::Serialize,
853    Copy,
854    Hash,
855    PartialOrd,
856    Ord,
857)]
858#[repr(i32)]
859pub enum WhatsThisHelp {
860    /// The application uses the F1 key to start Windows Help and load the topic
861    /// identified by the `help_context_id` property.
862    ///
863    /// This is the default setting.
864    #[default]
865    F1Help = 0,
866    /// The application uses one of the "What's This?" access techniques to
867    /// start Windows Help and load a topic identified by the
868    /// `help_context_id` property.
869    WhatsThisHelp = -1,
870}
871
872impl TryFrom<&str> for WhatsThisHelp {
873    type Error = ErrorKind;
874
875    fn try_from(value: &str) -> Result<Self, Self::Error> {
876        match value {
877            "0" => Ok(WhatsThisHelp::F1Help),
878            "-1" => Ok(WhatsThisHelp::WhatsThisHelp),
879            _ => Err(ErrorKind::Form(FormError::InvalidWhatsThisHelp {
880                value: value.to_string(),
881            })),
882        }
883    }
884}
885
886impl From<bool> for WhatsThisHelp {
887    fn from(value: bool) -> Self {
888        if value {
889            WhatsThisHelp::WhatsThisHelp
890        } else {
891            WhatsThisHelp::F1Help
892        }
893    }
894}
895
896impl FromStr for WhatsThisHelp {
897    type Err = ErrorKind;
898
899    fn from_str(s: &str) -> Result<Self, Self::Err> {
900        WhatsThisHelp::try_from(s)
901    }
902}
903
904impl Display for WhatsThisHelp {
905    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
906        let text = match self {
907            WhatsThisHelp::F1Help => "F1Help",
908            WhatsThisHelp::WhatsThisHelp => "WhatsThisHelp",
909        };
910        write!(f, "{text}")
911    }
912}
913
914/// Determines the type of link used for a DDE conversation and activates the
915/// connection.
916///
917/// Forms allow a destination application to initiate a conversation with a
918/// Visual Basic source form as specified by the destination applications
919/// `application**|topic!**item` expression.
920///
921/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa235154(v=vs.60))
922#[derive(
923    Debug,
924    PartialEq,
925    Eq,
926    Clone,
927    serde::Serialize,
928    Default,
929    TryFromPrimitive,
930    Copy,
931    Hash,
932    PartialOrd,
933    Ord,
934)]
935#[repr(i32)]
936pub enum FormLinkMode {
937    /// No DDE interaction. No destination application can initiate a conversation
938    /// with the source form as the topic, and no application can poke data to
939    /// the form.
940    ///
941    /// This is the default setting.
942    #[default]
943    None = 0,
944    /// Allows any `Label`, `PictureBox`, or `TextBox` control on a form to supply
945    /// data to any destination application that establishes a DDE conversation
946    /// with the form. If such a link exists, Visual Basic automatically
947    /// notifies the destination whenever the contents of a control are changed.
948    /// In addition, a destination application can poke data to any `Label`,
949    /// `PictureBox`, or `TextBox` control on the form.
950    Source = 1,
951}
952
953impl TryFrom<&str> for FormLinkMode {
954    type Error = ErrorKind;
955
956    fn try_from(value: &str) -> Result<Self, Self::Error> {
957        match value {
958            "0" => Ok(FormLinkMode::None),
959            "1" => Ok(FormLinkMode::Source),
960            _ => Err(ErrorKind::Form(FormError::InvalidFormLinkMode {
961                value: value.to_string(),
962            })),
963        }
964    }
965}
966
967impl FromStr for FormLinkMode {
968    type Err = ErrorKind;
969
970    fn from_str(s: &str) -> Result<Self, Self::Err> {
971        FormLinkMode::try_from(s)
972    }
973}
974
975impl From<bool> for FormLinkMode {
976    fn from(value: bool) -> Self {
977        if value {
978            FormLinkMode::Source
979        } else {
980            FormLinkMode::None
981        }
982    }
983}
984
985impl Display for FormLinkMode {
986    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
987        let text = match self {
988            FormLinkMode::None => "None",
989            FormLinkMode::Source => "Source",
990        };
991        write!(f, "{text}")
992    }
993}
994
995/// Controls the display state of a form from normal, minimized, or maximized.
996/// This is used with the `Form` and `MDIForm` controls.
997///
998/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa445778(v=vs.60))
999#[derive(
1000    Debug,
1001    PartialEq,
1002    Eq,
1003    Clone,
1004    serde::Serialize,
1005    Default,
1006    TryFromPrimitive,
1007    Copy,
1008    Hash,
1009    PartialOrd,
1010    Ord,
1011)]
1012#[repr(i32)]
1013pub enum WindowState {
1014    /// The form is in its normal state.
1015    ///
1016    /// This is the default setting.
1017    #[default]
1018    Normal = 0,
1019    /// The form is minimized (minimized to an icon0).
1020    Minimized = 1,
1021    /// The form is maximized (enlarged to maximum size).
1022    Maximized = 2,
1023}
1024
1025impl TryFrom<&str> for WindowState {
1026    type Error = ErrorKind;
1027
1028    fn try_from(value: &str) -> Result<Self, Self::Error> {
1029        match value {
1030            "0" => Ok(WindowState::Normal),
1031            "1" => Ok(WindowState::Minimized),
1032            "2" => Ok(WindowState::Maximized),
1033            _ => Err(ErrorKind::Form(FormError::InvalidWindowState {
1034                value: value.to_string(),
1035            })),
1036        }
1037    }
1038}
1039
1040impl FromStr for WindowState {
1041    type Err = ErrorKind;
1042
1043    fn from_str(s: &str) -> Result<Self, Self::Err> {
1044        WindowState::try_from(s)
1045    }
1046}
1047
1048impl Display for WindowState {
1049    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1050        let text = match self {
1051            WindowState::Normal => "Normal",
1052            WindowState::Minimized => "Minimized",
1053            WindowState::Maximized => "Maximized",
1054        };
1055        write!(f, "{text}")
1056    }
1057}
1058
1059/// The `StartUpPosition` property of a `Form` or `MDIForm` control determines
1060/// the initial position of the form when it first appears.
1061///
1062/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa445708(v=vs.60))
1063#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, Default, Copy, Hash, PartialOrd, Ord)]
1064pub enum StartUpPosition {
1065    /// The form is positioned based on the `client_height`, `client_width`,
1066    /// `client_top`, and `client_left` properties.
1067    ///
1068    /// The `Manual` variant is saved as a 0 in the VB6 file.
1069    Manual {
1070        /// The height of the client area of the form.
1071        client_height: i32,
1072        /// The width of the client area of the form.
1073        client_width: i32,
1074        /// The top position of the client area of the form.
1075        client_top: i32,
1076        /// The left position of the client area of the form.
1077        client_left: i32,
1078    },
1079    /// The form is centered in the parent window.
1080    ///
1081    /// The `CenterOwner` variant is saved as a 1 in the VB6 file.
1082    CenterOwner,
1083    /// The form is centered on the screen.
1084    ///
1085    /// The `CenterScreen` variant is saved as a 2 in the VB6 file.
1086    CenterScreen,
1087    #[default]
1088    /// Position in upper-left corner of screen.
1089    ///
1090    /// The `WindowsDefault` variant is saved as a 3 in the VB6 file.
1091    ///
1092    /// This is the default setting.
1093    WindowsDefault,
1094}
1095
1096impl Display for StartUpPosition {
1097    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1098        match self {
1099            StartUpPosition::Manual {
1100                client_height,
1101                client_width,
1102                client_top,
1103                client_left,
1104            } => write!(
1105                f,
1106                "Manual {{ client height: {client_height}, client width: {client_width}, client top: {client_top}, client left: {client_left} }}"
1107            ),
1108            StartUpPosition::CenterOwner => write!(f, "CenterOwner"),
1109            StartUpPosition::CenterScreen => write!(f, "CenterScreen"),
1110            StartUpPosition::WindowsDefault => write!(f, "WindowsDefault"),
1111        }
1112    }
1113}
1114
1115/// Represents either a reference to an external resource within a *.frx file or an embedded value.
1116///
1117/// This is used to represent properties that can either be stored directly within the VB6 form file
1118/// or as a reference to an external resource stored in the associated *.frx file.
1119///
1120/// The `Reference` variant contains the filename and offset within the *.frx file where the resource can be found.
1121/// The `Value` variant contains the actual value of type `T`.
1122///
1123/// This is useful for handling properties such as images, icons, or other binary data that may be
1124/// stored externally to keep the form file size manageable.
1125#[derive(Debug, PartialEq, Clone, Serialize)]
1126pub enum ReferenceOrValue<T> {
1127    Reference { filename: String, offset: u32 },
1128    Value(T),
1129}
1130
1131impl<T> Display for ReferenceOrValue<T> {
1132    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1133        match self {
1134            ReferenceOrValue::Reference { filename, offset } => {
1135                write!(f, "Reference {{ filename: {filename}, offset: {offset} }}",)
1136            }
1137            ReferenceOrValue::Value(_) => write!(f, "Value"),
1138        }
1139    }
1140}
1141
1142/// Represents a VB6 control.
1143#[derive(Debug, PartialEq, Clone, Serialize)]
1144pub struct Control {
1145    /// The name of the control.
1146    name: String,
1147    /// The tag of the control.
1148    tag: String,
1149    /// The index of the control.
1150    index: i32,
1151    /// The kind of control.
1152    kind: ControlKind,
1153}
1154
1155impl Control {
1156    /// Creates a new `Control` with the specified properties.
1157    ///
1158    /// # Arguments
1159    ///
1160    /// * `name` - The name of the control
1161    /// * `tag` - The tag of the control
1162    /// * `index` - The index of the control
1163    /// * `kind` - The kind of control
1164    ///
1165    /// # Returns
1166    ///
1167    /// A new `Control` instance.
1168    #[must_use]
1169    pub fn new(name: String, tag: String, index: i32, kind: ControlKind) -> Self {
1170        Self {
1171            name,
1172            tag,
1173            index,
1174            kind,
1175        }
1176    }
1177
1178    /// Returns the name of the control.
1179    #[must_use]
1180    pub fn name(&self) -> &str {
1181        &self.name
1182    }
1183
1184    /// Returns the tag of the control.
1185    #[must_use]
1186    pub fn tag(&self) -> &str {
1187        &self.tag
1188    }
1189
1190    /// Returns the index of the control.
1191    #[must_use]
1192    pub fn index(&self) -> i32 {
1193        self.index
1194    }
1195
1196    /// Returns a reference to the control kind.
1197    #[must_use]
1198    pub fn kind(&self) -> &ControlKind {
1199        &self.kind
1200    }
1201
1202    /// Consumes the control and returns its name.
1203    #[must_use]
1204    pub fn into_name(self) -> String {
1205        self.name
1206    }
1207
1208    /// Consumes the control and returns its tag.
1209    #[must_use]
1210    pub fn into_tag(self) -> String {
1211        self.tag
1212    }
1213
1214    /// Consumes the control and returns its kind.
1215    #[must_use]
1216    pub fn into_kind(self) -> ControlKind {
1217        self.kind
1218    }
1219
1220    /// Consumes the control and returns all of its parts as a tuple.
1221    ///
1222    /// # Returns
1223    ///
1224    /// A tuple containing `(name, tag, index, kind)`.
1225    #[must_use]
1226    pub fn into_parts(self) -> (String, String, i32, ControlKind) {
1227        (self.name, self.tag, self.index, self.kind)
1228    }
1229
1230    /// Sets the name of the control.
1231    ///
1232    /// This is primarily used during parsing when the control name needs to be
1233    /// updated based on attributes (e.g., `VB_Name` attribute in forms).
1234    pub fn set_name(&mut self, name: String) {
1235        self.name = name;
1236    }
1237}
1238
1239impl Display for Control {
1240    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1241        write!(f, "Control: {} ({})", self.name, self.kind)
1242    }
1243}
1244
1245/// The `ControlKind` determines the specific kind of control that the `Control` represents.
1246///
1247/// Each variant contains the properties that are specific to that kind of control.
1248#[derive(Debug, PartialEq, Clone, Serialize)]
1249pub enum ControlKind {
1250    /// A command button control.
1251    CommandButton {
1252        /// The properties of the command button control.
1253        properties: CommandButtonProperties,
1254    },
1255    /// A data control.
1256    Data {
1257        /// The properties of the data control.
1258        properties: DataProperties,
1259    },
1260    /// A text box control.
1261    TextBox {
1262        /// The properties of the text box control.
1263        properties: TextBoxProperties,
1264    },
1265    /// A check box control.
1266    CheckBox {
1267        /// The properties of the check box control.
1268        properties: CheckBoxProperties,
1269    },
1270    /// A line control.
1271    Line {
1272        /// The properties of the line control.
1273        properties: LineProperties,
1274    },
1275    /// A shape control.
1276    Shape {
1277        /// The properties of the shape control.
1278        properties: ShapeProperties,
1279    },
1280    /// A list box control.
1281    ListBox {
1282        /// The properties of the list box control.
1283        properties: ListBoxProperties,
1284    },
1285    /// A timer control.
1286    Timer {
1287        /// The properties of the timer control.
1288        properties: TimerProperties,
1289    },
1290    /// A label control.
1291    Label {
1292        /// The properties of the label control.
1293        properties: LabelProperties,
1294    },
1295    /// A frame control.
1296    Frame {
1297        /// The properties of the frame control.
1298        properties: FrameProperties,
1299        /// The child controls of the frame control.
1300        controls: Vec<Control>,
1301    },
1302    /// A picture box control.
1303    PictureBox {
1304        /// The properties of the picture box control.
1305        properties: PictureBoxProperties,
1306        /// The child controls of the picture box control.
1307        controls: Vec<Control>,
1308    },
1309    /// A file list box control.
1310    FileListBox {
1311        /// The properties of the file list box control.
1312        properties: FileListBoxProperties,
1313    },
1314    /// A drive list box control.
1315    DriveListBox {
1316        /// The properties of the drive list box control.
1317        properties: DriveListBoxProperties,
1318    },
1319    /// A directory list box control.
1320    DirListBox {
1321        /// The properties of the directory list box control.
1322        properties: DirListBoxProperties,
1323    },
1324    /// An OLE control.
1325    Ole {
1326        /// The properties of the OLE control.
1327        properties: OLEProperties,
1328    },
1329    /// An option button control.
1330    OptionButton {
1331        /// The properties of the option button control.
1332        properties: OptionButtonProperties,
1333    },
1334    /// An image control.
1335    Image {
1336        /// The properties of the image control.
1337        properties: ImageProperties,
1338    },
1339    /// A combo box control.
1340    ComboBox {
1341        /// The properties of the combo box control.
1342        properties: ComboBoxProperties,
1343    },
1344    /// A horizontal scroll bar control.
1345    HScrollBar {
1346        /// The properties of the horizontal scroll bar control.
1347        properties: ScrollBarProperties,
1348    },
1349    /// A vertical scroll bar control.
1350    VScrollBar {
1351        /// The properties of the vertical scroll bar control.
1352        properties: ScrollBarProperties,
1353    },
1354    /// A menu control.
1355    Menu {
1356        /// The properties of the menu control.
1357        properties: MenuProperties,
1358        /// The sub-menus of the menu control.
1359        sub_menus: Vec<MenuControl>,
1360    },
1361    /// A custom control.
1362    Custom {
1363        /// The properties of the custom control.
1364        properties: CustomControlProperties,
1365        /// The property groups of the custom control.
1366        property_groups: Vec<PropertyGroup>,
1367    },
1368}
1369
1370impl Display for ControlKind {
1371    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1372        match self {
1373            ControlKind::CommandButton { .. } => write!(f, "CommandButton"),
1374            ControlKind::Data { .. } => write!(f, "Data"),
1375            ControlKind::TextBox { .. } => write!(f, "TextBox"),
1376            ControlKind::CheckBox { .. } => write!(f, "CheckBox"),
1377            ControlKind::Line { .. } => write!(f, "Line"),
1378            ControlKind::Shape { .. } => write!(f, "Shape"),
1379            ControlKind::ListBox { .. } => write!(f, "ListBox"),
1380            ControlKind::Timer { .. } => write!(f, "Timer"),
1381            ControlKind::Label { .. } => write!(f, "Label"),
1382            ControlKind::Frame { .. } => write!(f, "Frame"),
1383            ControlKind::PictureBox { .. } => write!(f, "PictureBox"),
1384            ControlKind::FileListBox { .. } => write!(f, "FileListBox"),
1385            ControlKind::DriveListBox { .. } => write!(f, "DriveListBox"),
1386            ControlKind::DirListBox { .. } => write!(f, "DirListBox"),
1387            ControlKind::Ole { .. } => write!(f, "OLE"),
1388            ControlKind::OptionButton { .. } => write!(f, "OptionButton"),
1389            ControlKind::Image { .. } => write!(f, "Image"),
1390            ControlKind::ComboBox { .. } => write!(f, "ComboBox"),
1391            ControlKind::HScrollBar { .. } => write!(f, "HScrollBar"),
1392            ControlKind::VScrollBar { .. } => write!(f, "VScrollBar"),
1393            ControlKind::Menu { .. } => write!(f, "Menu"),
1394            ControlKind::Custom { .. } => write!(f, "Custom"),
1395        }
1396    }
1397}
1398
1399/// Helper methods for `ControlKind`.
1400impl ControlKind {
1401    /// Indicates if the control kind is a `Menu`.
1402    ///
1403    /// # Returns
1404    ///
1405    /// Returns `true` if the control kind is a `Menu`, otherwise `false`.
1406    #[must_use]
1407    pub fn is_menu(&self) -> bool {
1408        matches!(self, ControlKind::Menu { .. })
1409    }
1410
1411    /// Indicates if the control kind can contain child controls.
1412    ///
1413    /// # Returns
1414    ///
1415    /// Returns `true` if the control kind can contain child controls, otherwise `false`.
1416    #[must_use]
1417    pub fn can_contain_children(&self) -> bool {
1418        matches!(
1419            self,
1420            ControlKind::Frame { .. } | ControlKind::PictureBox { .. }
1421        )
1422    }
1423
1424    /// Indicates if the control kind can contain menus.
1425    ///
1426    /// # Returns
1427    ///
1428    /// Returns `true` if the control kind can contain menus, otherwise `false`.
1429    ///
1430    /// Note: This always returns `false` for `ControlKind` since `Form` and `MDIForm`
1431    /// are now top-level types (`FormRoot`), not control kinds.
1432    #[must_use]
1433    pub fn can_contain_menus(&self) -> bool {
1434        false
1435    }
1436
1437    /// Indicates if the control kind currently has menus.
1438    ///
1439    /// # Returns
1440    ///
1441    /// Returns `true` if the control kind has menus, otherwise `false`.
1442    ///
1443    /// Note: This always returns `false` for `ControlKind` since only `Form` and `MDIForm`
1444    /// can have menus, and they are now top-level types (`FormRoot`), not control kinds.
1445    #[must_use]
1446    pub fn has_menu(&self) -> bool {
1447        false
1448    }
1449
1450    /// Indicates if the control kind currently has child controls.
1451    ///
1452    /// # Returns
1453    ///
1454    /// Returns `true` if the control kind has child controls, otherwise `false`.
1455    #[must_use]
1456    pub fn has_children(&self) -> bool {
1457        matches!(
1458            self,
1459            ControlKind::Frame { controls, .. } |
1460            ControlKind::PictureBox { controls, .. } if !controls.is_empty()
1461        )
1462    }
1463
1464    /// Returns an iterator over child controls, if this control type supports children.
1465    ///
1466    /// # Returns
1467    ///
1468    /// An `Option` containing an iterator over child controls if the control kind supports children, otherwise `None`.
1469    ///
1470    /// Example:
1471    /// ```rust
1472    /// use vb6parse::*;
1473    /// use vb6parse::language::{Control, ControlKind, MenuControl, MenuProperties};
1474    ///
1475    /// let control = Control::new(
1476    ///     "MyFrame".to_string(),
1477    ///     "".to_string(),
1478    ///     0,
1479    ///     ControlKind::Frame {
1480    ///         properties: Default::default(),
1481    ///         controls: vec![],
1482    ///     },
1483    /// );
1484    ///
1485    /// if let Some(children) = control.kind().children() {
1486    ///     for child in children {
1487    ///         println!("Child control: {}", child.name());
1488    ///     }
1489    /// };
1490    /// ```
1491    #[must_use]
1492    pub fn children(&self) -> Option<impl Iterator<Item = &Control>> {
1493        match self {
1494            ControlKind::Frame { controls, .. } | ControlKind::PictureBox { controls, .. } => {
1495                Some(controls.iter())
1496            }
1497            _ => None,
1498        }
1499    }
1500
1501    /// Returns an iterator over menus, if this control type supports menus.
1502    ///
1503    /// # Returns
1504    ///
1505    /// An `Option` containing an iterator over menus if the control kind supports menus, otherwise `None`.
1506    ///
1507    /// Note: This always returns `None` for `ControlKind` since only `Form` and `MDIForm`
1508    /// can have menus, and they are now top-level types (`FormRoot`), not control kinds.
1509    ///
1510    /// Example:
1511    /// ```rust
1512    /// use vb6parse::language::{ControlKind, MenuControl, MenuProperties};
1513    ///
1514    /// // Menu controls can have sub-menus
1515    /// let menu_kind = ControlKind::Menu {
1516    ///     properties: MenuProperties {
1517    ///         caption: "File".to_string(),
1518    ///         ..Default::default()
1519    ///     },
1520    ///     sub_menus: vec![],
1521    /// };
1522    ///
1523    /// // Most control kinds don't support menus
1524    /// assert!(menu_kind.menus().is_none());
1525    /// ```
1526    /// can have menus, and they are now top-level types (`FormRoot`), not control kinds.
1527    #[must_use]
1528    pub fn menus(&self) -> Option<impl Iterator<Item = &MenuControl>> {
1529        None::<std::iter::Empty<&MenuControl>>
1530    }
1531
1532    /// Recursively iterates over all descendant controls, if this control type supports children.
1533    ///
1534    /// # Returns
1535    ///
1536    /// An iterator over all descendant controls.
1537    ///
1538    /// Example:
1539    /// ```rust
1540    /// use vb6parse::*;
1541    /// use vb6parse::language::{Control, ControlKind, MenuControl, MenuProperties};
1542    ///
1543    /// let control = Control::new(
1544    ///     "MyFrame".to_string(),
1545    ///     "".to_string(),
1546    ///     0,
1547    ///     ControlKind::Frame {
1548    ///         properties: Default::default(),
1549    ///         controls: vec![
1550    ///             Control::new(
1551    ///                 "Child1".to_string(),
1552    ///                 "".to_string(),
1553    ///                 0,
1554    ///                 ControlKind::Label {
1555    ///                     properties: Default::default(),
1556    ///                 },
1557    ///             ),
1558    ///             Control::new(
1559    ///                 "Child2".to_string(),
1560    ///                 "".to_string(),
1561    ///                 1,
1562    ///                 ControlKind::TextBox {
1563    ///                     properties: Default::default(),
1564    ///                 },
1565    ///             ),
1566    ///         ],
1567    ///     },
1568    /// );
1569    ///
1570    /// for child in control.kind().descendants() {
1571    ///     println!("Child control: {}", child.name());
1572    /// };
1573    /// ```
1574    #[must_use]
1575    pub fn descendants(&self) -> Box<dyn Iterator<Item = &Control> + '_> {
1576        Box::new(
1577            self.children()
1578                .into_iter()
1579                .flatten()
1580                .flat_map(|child| child.descendants()),
1581        )
1582    }
1583}
1584
1585impl Control {
1586    /// Returns `true` if the control is a `Menu`.
1587    #[must_use]
1588    pub fn is_menu(&self) -> bool {
1589        self.kind.is_menu()
1590    }
1591
1592    /// Returns `true` if the control has a menu.
1593    #[must_use]
1594    pub fn has_menu(&self) -> bool {
1595        self.kind.has_menu()
1596    }
1597
1598    /// Returns an iterator over menus, if this control type supports menus.
1599    ///
1600    /// # Returns
1601    ///
1602    /// An `Option` containing an iterator over menus if the control supports menus, otherwise `None`.
1603    ///
1604    /// Note: This always returns `None` since only `Form` and `MDIForm` can have menus,
1605    /// and they are now top-level types (`FormRoot`), not control kinds.
1606    ///
1607    /// Example:
1608    /// ```rust
1609    /// use vb6parse::language::{Control, ControlKind};
1610    ///
1611    /// let control = Control::new(
1612    ///     "MyButton".to_string(),
1613    ///     "".to_string(),
1614    ///     0,
1615    ///     ControlKind::CommandButton {
1616    ///         properties: Default::default(),
1617    ///     },
1618    /// );
1619    ///
1620    /// // Regular controls don't support menus
1621    /// assert!(control.menus().is_none());
1622    /// ```
1623    #[must_use]
1624    pub fn menus(&self) -> Option<impl Iterator<Item = &MenuControl>> {
1625        self.kind.menus()
1626    }
1627
1628    /// Returns true if this control type can contain menus.
1629    #[must_use]
1630    pub fn can_contain_menus(&self) -> bool {
1631        self.kind.can_contain_menus()
1632    }
1633
1634    /// Returns true if this control type can contain child controls.
1635    #[must_use]
1636    pub fn can_contain_children(&self) -> bool {
1637        self.kind.can_contain_children()
1638    }
1639
1640    /// Returns an iterator over child controls, if this control type supports children.
1641    ///
1642    /// # Returns
1643    ///
1644    /// An `Option` containing an iterator over child controls if the control supports children, otherwise `None`.
1645    ///
1646    /// Example:
1647    /// ```rust
1648    /// use vb6parse::*;
1649    /// use vb6parse::language::{Control, ControlKind, MenuControl, MenuProperties};
1650    ///
1651    /// let control = Control::new(
1652    ///     "MyFrame".to_string(),
1653    ///     "".to_string(),
1654    ///     0,
1655    ///     ControlKind::Frame {
1656    ///         properties: Default::default(),
1657    ///         controls: vec![],
1658    ///     },
1659    /// );
1660    ///
1661    /// if let Some(children) = control.children() {
1662    ///     for child in children {
1663    ///         println!("Child control: {}", child.name());
1664    ///     }
1665    /// };
1666    /// ```
1667    #[must_use]
1668    pub fn children(&self) -> Option<impl Iterator<Item = &Control>> {
1669        self.kind.children()
1670    }
1671
1672    /// Returns true if this control type can contain child controls.
1673    #[must_use]
1674    pub fn has_children(&self) -> bool {
1675        matches!(
1676            self.kind,
1677            ControlKind::Frame { .. } | ControlKind::PictureBox { .. }
1678        )
1679    }
1680
1681    /// Recursively iterates over this control and all descendants.
1682    ///
1683    /// # Returns
1684    ///
1685    /// An iterator over this control and all descendant controls.
1686    ///
1687    /// Example:
1688    /// ```rust
1689    /// use vb6parse::*;
1690    /// use vb6parse::language::{Control, ControlKind, MenuControl, MenuProperties};
1691    ///
1692    /// let control = Control::new(
1693    ///     "MyFrame".to_string(),
1694    ///     "".to_string(),
1695    ///     0,
1696    ///     ControlKind::Frame {
1697    ///         properties: Default::default(),
1698    ///         controls: vec![
1699    ///             Control::new(
1700    ///                 "Child1".to_string(),
1701    ///                 "".to_string(),
1702    ///                 0,
1703    ///                 ControlKind::Label {
1704    ///                     properties: Default::default(),
1705    ///                 },
1706    ///             ),
1707    ///             Control::new(
1708    ///                 "Child2".to_string(),
1709    ///                 "".to_string(),
1710    ///                 1,
1711    ///                 ControlKind::TextBox {
1712    ///                     properties: Default::default(),
1713    ///                 },
1714    ///             ),
1715    ///         ],
1716    ///     },
1717    /// );
1718    ///
1719    /// let mut descendants = control.descendants();
1720    /// while let Some(descendant) = descendants.next() {
1721    ///     println!("Descendant control: {}", descendant.name());
1722    /// }
1723    /// ```
1724    #[must_use]
1725    pub fn descendants(&self) -> Box<dyn Iterator<Item = &Control> + '_> {
1726        Box::new(std::iter::once(self).chain(self.kind.descendants()))
1727    }
1728}
1729
1730/// Determines whether an object is displayed in any size anywhere on a form or
1731/// whether it's displayed at the top, bottom, left, or right of the form and is
1732/// automatically sized to fit the form's width.
1733///
1734/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa267259(v=vs.60))
1735#[derive(
1736    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
1737)]
1738#[repr(i32)]
1739pub enum Align {
1740    /// The control is not docked to any side of the parent control.
1741    /// This setting is ignored if the object is on an `MDIForm`.
1742    ///
1743    /// This is the default setting in a non-MDI form.
1744    #[default]
1745    None = 0,
1746    /// The top of the control is at the top of the form, and its width is equal
1747    /// to the form's `ScaleWidth` property setting.
1748    ///
1749    /// This is the default setting in an MDI form.
1750    Top = 1,
1751    /// The bottom of the control is at the bottom of the form, and its width is
1752    /// equal to the form's `ScaleWidth` property setting.
1753    Bottom = 2,
1754    /// The left side of the control is at the left of the form, and its width
1755    /// is equal to the form's `ScaleWidth` property setting.
1756    Left = 3,
1757    /// The right side of the control is at the right of the form, and its width
1758    /// is equal to the form's `ScaleWidth` property setting.
1759    Right = 4,
1760}
1761
1762impl TryFrom<&str> for Align {
1763    type Error = ErrorKind;
1764
1765    fn try_from(value: &str) -> Result<Self, Self::Error> {
1766        match value {
1767            "0" => Ok(Align::None),
1768            "1" => Ok(Align::Top),
1769            "2" => Ok(Align::Bottom),
1770            "3" => Ok(Align::Left),
1771            "4" => Ok(Align::Right),
1772            _ => Err(ErrorKind::Form(FormError::InvalidAlign {
1773                value: value.to_string(),
1774            })),
1775        }
1776    }
1777}
1778
1779impl FromStr for Align {
1780    type Err = ErrorKind;
1781
1782    fn from_str(s: &str) -> Result<Self, Self::Err> {
1783        Align::try_from(s)
1784    }
1785}
1786
1787impl Display for Align {
1788    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1789        let text = match self {
1790            Align::None => "None",
1791            Align::Top => "Top",
1792            Align::Bottom => "Bottom",
1793            Align::Left => "Left",
1794            Align::Right => "Right",
1795        };
1796        write!(f, "{text}")
1797    }
1798}
1799
1800/// Determines the alignment of a `CheckBox` or `OptionButton` control.
1801///
1802/// This enum is the 'Alignment' property in VB6 specifically for `CheckBox` and
1803/// `OptionButton` controls only.
1804///
1805/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa267261(v=vs.60))
1806#[derive(
1807    Debug, PartialEq, Eq, Clone, Serialize, TryFromPrimitive, Default, Copy, Hash, PartialOrd, Ord,
1808)]
1809#[repr(i32)]
1810pub enum JustifyAlignment {
1811    /// The text is left-aligned. The control is right-aligned.
1812    ///
1813    /// This is the default setting.
1814    #[default]
1815    LeftJustify = 0,
1816    /// The text is right-aligned. The control is left-aligned.
1817    RightJustify = 1,
1818}
1819
1820impl TryFrom<&str> for JustifyAlignment {
1821    type Error = ErrorKind;
1822
1823    fn try_from(value: &str) -> Result<Self, Self::Error> {
1824        match value {
1825            "0" => Ok(JustifyAlignment::LeftJustify),
1826            "1" => Ok(JustifyAlignment::RightJustify),
1827            _ => Err(ErrorKind::Form(FormError::InvalidJustifyAlignment {
1828                value: value.to_string(),
1829            })),
1830        }
1831    }
1832}
1833
1834impl FromStr for JustifyAlignment {
1835    type Err = ErrorKind;
1836
1837    fn from_str(s: &str) -> Result<Self, Self::Err> {
1838        JustifyAlignment::try_from(s)
1839    }
1840}
1841
1842impl Display for JustifyAlignment {
1843    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1844        let text = match self {
1845            JustifyAlignment::LeftJustify => "Left Justify",
1846            JustifyAlignment::RightJustify => "Right Justify",
1847        };
1848        write!(f, "{text}")
1849    }
1850}
1851
1852/// The `Alignment` property of a `Label` and `TextBox` control determines
1853/// the alignment of the text within the control. The `Alignment` property is used
1854/// to specify how the text is aligned within the control, such as left-aligned,
1855/// right-aligned, or centered.
1856///
1857/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa267261(v=vs.60))
1858#[derive(
1859    Debug, PartialEq, Eq, Clone, Serialize, TryFromPrimitive, Default, Copy, Hash, PartialOrd, Ord,
1860)]
1861#[repr(i32)]
1862pub enum Alignment {
1863    /// The text is left-aligned within the control.
1864    ///
1865    /// This is the default setting.
1866    #[default]
1867    LeftJustify = 0,
1868    /// The text is right-aligned within the control.
1869    RightJustify = 1,
1870    /// The text is centered within the control.
1871    Center = 2,
1872}
1873
1874impl TryFrom<&str> for Alignment {
1875    type Error = ErrorKind;
1876
1877    fn try_from(value: &str) -> Result<Self, Self::Error> {
1878        match value {
1879            "0" => Ok(Alignment::LeftJustify),
1880            "1" => Ok(Alignment::RightJustify),
1881            "2" => Ok(Alignment::Center),
1882            _ => Err(ErrorKind::Form(FormError::InvalidAlignment {
1883                value: value.to_string(),
1884            })),
1885        }
1886    }
1887}
1888
1889impl FromStr for Alignment {
1890    type Err = ErrorKind;
1891
1892    fn from_str(s: &str) -> Result<Self, Self::Err> {
1893        Alignment::try_from(s)
1894    }
1895}
1896
1897impl Display for Alignment {
1898    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1899        let text = match self {
1900            Alignment::LeftJustify => "LeftJustify",
1901            Alignment::RightJustify => "RightJustify",
1902            Alignment::Center => "Center",
1903        };
1904        write!(f, "{text}")
1905    }
1906}
1907
1908/// Indicates whether a `Label` control or the background of a `Shape` control
1909/// is transparent or opaque.
1910///
1911/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa245038(v=vs.60))
1912#[derive(
1913    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
1914)]
1915#[repr(i32)]
1916pub enum BackStyle {
1917    /// The transparent background color and any graphics are visible behind the
1918    /// control.
1919    Transparent = 0,
1920    /// The control's `BackColor` property setting fills the control and
1921    /// obscures any color or graphics behind it.
1922    ///
1923    /// This is the default setting.
1924    #[default]
1925    Opaque = 1,
1926}
1927
1928impl TryFrom<&str> for BackStyle {
1929    type Error = ErrorKind;
1930
1931    fn try_from(value: &str) -> Result<Self, Self::Error> {
1932        match value {
1933            "0" => Ok(BackStyle::Transparent),
1934            "1" => Ok(BackStyle::Opaque),
1935            _ => Err(ErrorKind::Form(FormError::InvalidBackStyle {
1936                value: value.to_string(),
1937            })),
1938        }
1939    }
1940}
1941
1942impl FromStr for BackStyle {
1943    type Err = ErrorKind;
1944
1945    fn from_str(s: &str) -> Result<Self, Self::Err> {
1946        BackStyle::try_from(s)
1947    }
1948}
1949
1950impl Display for BackStyle {
1951    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1952        let text = match self {
1953            BackStyle::Transparent => "Transparent",
1954            BackStyle::Opaque => "Opaque",
1955        };
1956        write!(f, "{text}")
1957    }
1958}
1959
1960/// The `Appearance` determines whether or not a control is painted at run time
1961/// with 3D effects.
1962///
1963/// Note:
1964///
1965/// If set to `ThreeD` (1) at design time, the `Appearance` property draws
1966/// controls with three-dimensional effects. If the form's `BorderStyle`
1967/// property is set to `FixedDouble` (vbFixedDouble, or 3), the caption and
1968/// border of the form are also painted with three-dimensional effects.
1969///
1970/// Setting the `Appearance` property to `ThreeD` (1) also causes the form and its
1971/// controls to have their `BackColor` property set to the color selected for 3D
1972/// Objects in the `Appearance` tab of the operating system's Display Properties
1973/// dialog box.
1974///
1975/// Setting the `Appearance` property to `ThreeD` (1) for an `MDIForm` object
1976/// affects only the MDI parent form. To have three-dimensional effects on MDI
1977/// child forms, you must set each child form's `Appearance` property to
1978/// `ThreeD` (1).
1979///
1980/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa244932(v=vs.60))
1981#[derive(
1982    Debug, PartialEq, Eq, Clone, Serialize, TryFromPrimitive, Default, Copy, Hash, PartialOrd, Ord,
1983)]
1984#[repr(i32)]
1985pub enum Appearance {
1986    /// The control is painted with a flat style.
1987    Flat = 0,
1988    /// The control is painted with a 3D style.
1989    ///
1990    /// This is the default setting.
1991    #[default]
1992    ThreeD = 1,
1993}
1994
1995impl TryFrom<&str> for Appearance {
1996    type Error = ErrorKind;
1997
1998    fn try_from(value: &str) -> Result<Self, Self::Error> {
1999        match value {
2000            "0" => Ok(Appearance::Flat),
2001            "1" => Ok(Appearance::ThreeD),
2002            _ => Err(ErrorKind::Form(FormError::InvalidAppearance {
2003                value: value.to_string(),
2004            })),
2005        }
2006    }
2007}
2008
2009impl FromStr for Appearance {
2010    type Err = ErrorKind;
2011
2012    fn from_str(s: &str) -> Result<Self, Self::Err> {
2013        Appearance::try_from(s)
2014    }
2015}
2016
2017impl Display for Appearance {
2018    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2019        let text = match self {
2020            Appearance::Flat => "Flat",
2021            Appearance::ThreeD => "ThreeD",
2022        };
2023        write!(f, "{text}")
2024    }
2025}
2026
2027/// The `BorderStyle` determines the appearance of the border of a control.
2028///
2029/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa245047(v=vs.60))
2030#[derive(
2031    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2032)]
2033#[repr(i32)]
2034pub enum BorderStyle {
2035    /// The control has no border.
2036    ///
2037    /// This is the default setting for `Image` and `Label` controls.
2038    None = 0,
2039    /// The control has a single-line border.
2040    ///
2041    /// This is the default setting for `PictureBox`, `TextBox`, `OLE` container
2042    /// controls.
2043    #[default]
2044    FixedSingle = 1,
2045}
2046
2047impl TryFrom<&str> for BorderStyle {
2048    type Error = ErrorKind;
2049
2050    fn try_from(value: &str) -> Result<Self, Self::Error> {
2051        match value {
2052            "0" => Ok(BorderStyle::None),
2053            "1" => Ok(BorderStyle::FixedSingle),
2054            _ => Err(ErrorKind::Form(FormError::InvalidBorderStyle {
2055                value: value.to_string(),
2056            })),
2057        }
2058    }
2059}
2060
2061impl FromStr for BorderStyle {
2062    type Err = ErrorKind;
2063
2064    fn from_str(s: &str) -> Result<Self, Self::Err> {
2065        BorderStyle::try_from(s)
2066    }
2067}
2068
2069impl Display for BorderStyle {
2070    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2071        let text = match self {
2072            BorderStyle::None => "None",
2073            BorderStyle::FixedSingle => "FixedSingle",
2074        };
2075        write!(f, "{text}")
2076    }
2077}
2078
2079/// Determines the style of drag and drop operations.
2080#[derive(
2081    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2082)]
2083#[repr(i32)]
2084pub enum DragMode {
2085    /// The control does not support drag and drop operations until
2086    /// the program manually initiates the drag operation.
2087    ///
2088    /// This is the default setting.
2089    #[default]
2090    Manual = 0,
2091    /// The control automatically initiates a drag operation when the
2092    /// user presses the mouse button on the control.
2093    Automatic = 1,
2094}
2095
2096impl TryFrom<&str> for DragMode {
2097    type Error = ErrorKind;
2098
2099    fn try_from(value: &str) -> Result<Self, Self::Error> {
2100        match value {
2101            "0" => Ok(DragMode::Manual),
2102            "1" => Ok(DragMode::Automatic),
2103            _ => Err(ErrorKind::Form(FormError::InvalidDragMode {
2104                value: value.to_string(),
2105            })),
2106        }
2107    }
2108}
2109
2110impl FromStr for DragMode {
2111    type Err = ErrorKind;
2112
2113    fn from_str(s: &str) -> Result<Self, Self::Err> {
2114        DragMode::try_from(s)
2115    }
2116}
2117
2118impl Display for DragMode {
2119    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2120        let text = match self {
2121            DragMode::Manual => "Manual",
2122            DragMode::Automatic => "Automatic",
2123        };
2124        write!(f, "{text}")
2125    }
2126}
2127
2128/// Specifies how the pen (the color used in drawing) interacts with the
2129/// background.
2130#[derive(
2131    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2132)]
2133#[repr(i32)]
2134pub enum DrawMode {
2135    /// Black pen color is applied over the background.
2136    Blackness = 1,
2137    /// Inversion is applied after the combination of the pen and the background color.
2138    NotMergePen = 2,
2139    /// The combination of the colors common to the background color and the inverse of the pen.
2140    MaskNotPen = 3,
2141    /// Inversion is applied to the pen color.
2142    NotCopyPen = 4,
2143    /// The combination of the colors common to the pen and the inverse of the background color.
2144    MaskPenNot = 5,
2145    /// Inversion is applied to the background color.
2146    Invert = 6,
2147    /// The combination of the colors common to the pen and the background color, but not in both (ie, XOR).
2148    XorPen = 7,
2149    /// Inversion is applied to the combination of the colors common to both the pen and the background color.
2150    NotMaskPen = 8,
2151    /// The combination of the colors common to the pen and the background color.
2152    MaskPen = 9,
2153    /// Inversion of the combination of the colors in the pen and the background color but not in both (ie, NXOR).
2154    NotXorPen = 10,
2155    /// No operation is performed. The output remains unchanged. In effect, this turns drawing off (No Operation).
2156    Nop = 11,
2157    /// The combination of the display color and the inverse of the pen color.
2158    MergeNotPen = 12,
2159    /// The color specified by the `ForeColor` property is applied over the background.
2160    ///
2161    /// This is the default setting.
2162    #[default]
2163    CopyPen = 13,
2164    /// The combination of the pen color and inverse of the display color.
2165    MergePenNot = 14,
2166    /// the combination of the pen color and the display color.
2167    MergePen = 15,
2168    /// White pen color is applied over the background.
2169    Whiteness = 16,
2170}
2171
2172impl TryFrom<&str> for DrawMode {
2173    type Error = ErrorKind;
2174
2175    fn try_from(value: &str) -> Result<Self, Self::Error> {
2176        match value {
2177            "1" => Ok(DrawMode::Blackness),
2178            "2" => Ok(DrawMode::NotMergePen),
2179            "3" => Ok(DrawMode::MaskNotPen),
2180            "4" => Ok(DrawMode::NotCopyPen),
2181            "5" => Ok(DrawMode::MaskPenNot),
2182            "6" => Ok(DrawMode::Invert),
2183            "7" => Ok(DrawMode::XorPen),
2184            "8" => Ok(DrawMode::NotMaskPen),
2185            "9" => Ok(DrawMode::MaskPen),
2186            "10" => Ok(DrawMode::NotXorPen),
2187            "11" => Ok(DrawMode::Nop),
2188            "12" => Ok(DrawMode::MergeNotPen),
2189            "13" => Ok(DrawMode::CopyPen),
2190            "14" => Ok(DrawMode::MergePenNot),
2191            "15" => Ok(DrawMode::MergePen),
2192            "16" => Ok(DrawMode::Whiteness),
2193            _ => Err(ErrorKind::Form(FormError::InvalidDrawMode {
2194                value: value.to_string(),
2195            })),
2196        }
2197    }
2198}
2199
2200impl FromStr for DrawMode {
2201    type Err = ErrorKind;
2202
2203    fn from_str(s: &str) -> Result<Self, Self::Err> {
2204        DrawMode::try_from(s)
2205    }
2206}
2207
2208impl Display for DrawMode {
2209    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2210        let text = match self {
2211            DrawMode::Blackness => "Blackness",
2212            DrawMode::NotMergePen => "Not Merge Pen",
2213            DrawMode::MaskNotPen => "Mask Not Pen",
2214            DrawMode::NotCopyPen => "Not Copy Pen",
2215            DrawMode::MaskPenNot => "Mask Pen Not",
2216            DrawMode::Invert => "Invert",
2217            DrawMode::XorPen => "Xor Pen",
2218            DrawMode::NotMaskPen => "Not Mask Pen",
2219            DrawMode::MaskPen => "Mask Pen",
2220            DrawMode::NotXorPen => "Not Xor Pen",
2221            DrawMode::Nop => "Nop",
2222            DrawMode::MergeNotPen => "Merge Not Pen",
2223            DrawMode::CopyPen => "Copy Pen",
2224            DrawMode::MergePenNot => "Merge Pen Not",
2225            DrawMode::MergePen => "Merge Pen",
2226            DrawMode::Whiteness => "Whiteness",
2227        };
2228        write!(f, "{text}")
2229    }
2230}
2231
2232/// Determines the line style of any drawing from any graphic method applied by the control.
2233#[derive(
2234    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2235)]
2236#[repr(i32)]
2237pub enum DrawStyle {
2238    /// A solid line.
2239    ///
2240    /// This is the default setting.
2241    #[default]
2242    Solid = 0,
2243    /// A dashed line.
2244    Dash = 1,
2245    /// A dotted line.
2246    Dot = 2,
2247    /// A line that alternates between dashes and dots.
2248    DashDot = 3,
2249    /// A line that alternates between dashes and double dots.
2250    DashDotDot = 4,
2251    /// Invisible line, transparent interior.
2252    Transparent = 5,
2253    /// Invisible line, solid interior.
2254    InsideSolid = 6,
2255}
2256
2257impl TryFrom<&str> for DrawStyle {
2258    type Error = ErrorKind;
2259
2260    fn try_from(value: &str) -> Result<Self, Self::Error> {
2261        match value {
2262            "0" => Ok(DrawStyle::Solid),
2263            "1" => Ok(DrawStyle::Dash),
2264            "2" => Ok(DrawStyle::Dot),
2265            "3" => Ok(DrawStyle::DashDot),
2266            "4" => Ok(DrawStyle::DashDotDot),
2267            "5" => Ok(DrawStyle::Transparent),
2268            "6" => Ok(DrawStyle::InsideSolid),
2269            _ => Err(ErrorKind::Form(FormError::InvalidDrawStyle {
2270                value: value.to_string(),
2271            })),
2272        }
2273    }
2274}
2275
2276impl FromStr for DrawStyle {
2277    type Err = ErrorKind;
2278
2279    fn from_str(s: &str) -> Result<Self, Self::Err> {
2280        DrawStyle::try_from(s)
2281    }
2282}
2283
2284impl Display for DrawStyle {
2285    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2286        let text = match self {
2287            DrawStyle::Solid => "Solid",
2288            DrawStyle::Dash => "Dash",
2289            DrawStyle::Dot => "Dot",
2290            DrawStyle::DashDot => "DashDot",
2291            DrawStyle::DashDotDot => "DashDotDot",
2292            DrawStyle::Transparent => "Transparent",
2293            DrawStyle::InsideSolid => "InsideSolid",
2294        };
2295        write!(f, "{text}")
2296    }
2297}
2298
2299/// Determines the appearance of the mouse pointer when it is over the control.
2300#[derive(
2301    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2302)]
2303#[repr(i32)]
2304pub enum MousePointer {
2305    /// Standard pointer. The image is determined by the hovered over object.
2306    ///
2307    /// This is the default setting.
2308    #[default]
2309    Default = 0,
2310    /// Arrow pointer.
2311    Arrow = 1,
2312    /// Cross-hair pointer.
2313    Cross = 2,
2314    /// I-beam pointer.
2315    IBeam = 3,
2316    /// Icon pointer. The image is determined by the `MouseIcon` property.
2317    /// If the `MouseIcon` property is not set, the behavior is the same as the Default setting.
2318    /// This is a duplicate of Custom (99).
2319    Icon = 4,
2320    /// Size all cursor (arrows pointing north, south, east, and west).
2321    /// This cursor is used to indicate that the control can be resized in any direction.
2322    Size = 5,
2323    /// Double arrow pointing northeast and southwest.
2324    SizeNESW = 6,
2325    /// Double arrow pointing north and south.
2326    SizeNS = 7,
2327    /// Double arrow pointing northwest and southeast.
2328    SizeNWSE = 8,
2329    /// Double arrow pointing west and east.
2330    SizeWE = 9,
2331    /// Up arrow.
2332    UpArrow = 10,
2333    /// Hourglass or wait cursor.
2334    Hourglass = 11,
2335    /// "Not" symbol (circle with a diagonal line) on top of the object being dragged.
2336    /// Indicates an invalid drop target.
2337    NoDrop = 12,
2338    /// Arrow with an hourglass.
2339    ArrowHourglass = 13,
2340    /// Arrow with a question mark.
2341    ArrowQuestion = 14,
2342    /// Size all cursor (arrows pointing north, south, east, and west).
2343    /// This cursor is used to indicate that the control can be resized in any direction.
2344    /// Duplicate of Size (5).
2345    SizeAll = 15,
2346    /// Uses the icon specified by the `MouseIcon` property.
2347    /// If the `MouseIcon` property is not set, the behavior is the same as the Default setting.
2348    /// This is a duplicate of Icon (4).
2349    Custom = 99,
2350}
2351
2352impl TryFrom<&str> for MousePointer {
2353    type Error = ErrorKind;
2354
2355    fn try_from(value: &str) -> Result<Self, Self::Error> {
2356        match value {
2357            "0" => Ok(MousePointer::Default),
2358            "1" => Ok(MousePointer::Arrow),
2359            "2" => Ok(MousePointer::Cross),
2360            "3" => Ok(MousePointer::IBeam),
2361            "4" => Ok(MousePointer::Icon),
2362            "5" => Ok(MousePointer::Size),
2363            "6" => Ok(MousePointer::SizeNESW),
2364            "7" => Ok(MousePointer::SizeNS),
2365            "8" => Ok(MousePointer::SizeNWSE),
2366            "9" => Ok(MousePointer::SizeWE),
2367            "10" => Ok(MousePointer::UpArrow),
2368            "11" => Ok(MousePointer::Hourglass),
2369            "12" => Ok(MousePointer::NoDrop),
2370            "13" => Ok(MousePointer::ArrowHourglass),
2371            "14" => Ok(MousePointer::ArrowQuestion),
2372            "15" => Ok(MousePointer::SizeAll),
2373            "99" => Ok(MousePointer::Custom),
2374            _ => Err(ErrorKind::Form(FormError::InvalidMousePointer {
2375                value: value.to_string(),
2376            })),
2377        }
2378    }
2379}
2380
2381impl FromStr for MousePointer {
2382    type Err = ErrorKind;
2383
2384    fn from_str(s: &str) -> Result<Self, Self::Err> {
2385        MousePointer::try_from(s)
2386    }
2387}
2388
2389impl Display for MousePointer {
2390    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2391        let text = match self {
2392            MousePointer::Default => "Default",
2393            MousePointer::Arrow => "Arrow",
2394            MousePointer::Cross => "Cross",
2395            MousePointer::IBeam => "IBeam",
2396            MousePointer::Icon => "Icon",
2397            MousePointer::Size => "Size",
2398            MousePointer::SizeNESW => "SizeNESW",
2399            MousePointer::SizeNS => "SizeNS",
2400            MousePointer::SizeNWSE => "SizeNWSE",
2401            MousePointer::SizeWE => "SizeWE",
2402            MousePointer::UpArrow => "UpArrow",
2403            MousePointer::Hourglass => "Hourglass",
2404            MousePointer::NoDrop => "NoDrop",
2405            MousePointer::ArrowHourglass => "ArrowHourglass",
2406            MousePointer::ArrowQuestion => "ArrowQuestion",
2407            MousePointer::SizeAll => "SizeAll",
2408            MousePointer::Custom => "Custom",
2409        };
2410        write!(f, "{text}")
2411    }
2412}
2413
2414/// Determines the style of drag and drop operations.
2415#[derive(
2416    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2417)]
2418#[repr(i32)]
2419pub enum OLEDragMode {
2420    /// The programmer handles all OLE drag/drop events manually.
2421    ///
2422    /// This is the default setting.
2423    #[default]
2424    Manual = 0,
2425    /// The control automatically handles all OLE drag/drop events.
2426    Automatic = 1,
2427}
2428
2429impl TryFrom<&str> for OLEDragMode {
2430    type Error = ErrorKind;
2431
2432    fn try_from(value: &str) -> Result<Self, Self::Error> {
2433        match value {
2434            "0" => Ok(OLEDragMode::Manual),
2435            "1" => Ok(OLEDragMode::Automatic),
2436            _ => Err(ErrorKind::Form(FormError::InvalidOLEDragMode {
2437                value: value.to_string(),
2438            })),
2439        }
2440    }
2441}
2442
2443impl FromStr for OLEDragMode {
2444    type Err = ErrorKind;
2445
2446    fn from_str(s: &str) -> Result<Self, Self::Err> {
2447        OLEDragMode::try_from(s)
2448    }
2449}
2450
2451impl Display for OLEDragMode {
2452    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2453        let text = match self {
2454            OLEDragMode::Manual => "Manual",
2455            OLEDragMode::Automatic => "Automatic",
2456        };
2457        write!(f, "{text}")
2458    }
2459}
2460
2461/// Determines the style of drop operations.
2462#[derive(
2463    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2464)]
2465#[repr(i32)]
2466pub enum OLEDropMode {
2467    /// The control does not accept any OLE drop operations.
2468    ///
2469    /// This is the default setting.
2470    #[default]
2471    None = 0,
2472    /// The programmer handles all OLE drop events manually.
2473    Manual = 1,
2474}
2475
2476impl TryFrom<&str> for OLEDropMode {
2477    type Error = ErrorKind;
2478
2479    fn try_from(value: &str) -> Result<Self, Self::Error> {
2480        match value {
2481            "0" => Ok(OLEDropMode::None),
2482            "1" => Ok(OLEDropMode::Manual),
2483            _ => Err(ErrorKind::Form(FormError::InvalidOLEDropMode {
2484                value: value.to_string(),
2485            })),
2486        }
2487    }
2488}
2489
2490impl FromStr for OLEDropMode {
2491    type Err = ErrorKind;
2492
2493    fn from_str(s: &str) -> Result<Self, Self::Err> {
2494        OLEDropMode::try_from(s)
2495    }
2496}
2497
2498impl Display for OLEDropMode {
2499    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2500        let text = match self {
2501            OLEDropMode::None => "None",
2502            OLEDropMode::Manual => "Manual",
2503        };
2504        write!(f, "{text}")
2505    }
2506}
2507
2508/// Determines if the control is clipped to the bounds of the parent control.
2509/// This is used with the `Form` and `MDIForm` controls.
2510#[derive(
2511    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2512)]
2513#[repr(i32)]
2514pub enum ClipControls {
2515    /// The controls are not clipped to the bounds of the parent control.
2516    Unbounded = 0,
2517    /// The controls are clipped to the bounds of the parent control.
2518    ///
2519    /// This is the default setting.
2520    #[default]
2521    Clipped = 1,
2522}
2523
2524impl TryFrom<&str> for ClipControls {
2525    type Error = ErrorKind;
2526
2527    fn try_from(value: &str) -> Result<Self, Self::Error> {
2528        match value {
2529            "0" => Ok(ClipControls::Unbounded),
2530            "1" => Ok(ClipControls::Clipped),
2531            _ => Err(ErrorKind::Form(FormError::InvalidClipControls {
2532                value: value.to_string(),
2533            })),
2534        }
2535    }
2536}
2537
2538impl FromStr for ClipControls {
2539    type Err = ErrorKind;
2540
2541    fn from_str(s: &str) -> Result<Self, Self::Err> {
2542        ClipControls::try_from(s)
2543    }
2544}
2545
2546impl Display for ClipControls {
2547    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2548        let text = match self {
2549            ClipControls::Unbounded => "Unbounded",
2550            ClipControls::Clipped => "Clipped",
2551        };
2552        write!(f, "{text}")
2553    }
2554}
2555
2556/// Determines if the control uses standard styling or if it uses graphical styling from it's
2557/// picture properties.
2558#[derive(
2559    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2560)]
2561#[repr(i32)]
2562pub enum Style {
2563    /// The control uses standard styling.
2564    ///
2565    /// This is the default setting.
2566    #[default]
2567    Standard = 0,
2568    /// The control uses graphical styling using its appropriate picture properties.
2569    Graphical = 1,
2570}
2571
2572impl TryFrom<&str> for Style {
2573    type Error = ErrorKind;
2574
2575    fn try_from(value: &str) -> Result<Self, Self::Error> {
2576        match value {
2577            "0" => Ok(Style::Standard),
2578            "1" => Ok(Style::Graphical),
2579            _ => Err(ErrorKind::Form(FormError::InvalidStyle {
2580                value: value.to_string(),
2581            })),
2582        }
2583    }
2584}
2585
2586impl FromStr for Style {
2587    type Err = ErrorKind;
2588
2589    fn from_str(s: &str) -> Result<Self, Self::Err> {
2590        Style::try_from(s)
2591    }
2592}
2593
2594impl Display for Style {
2595    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2596        let text = match self {
2597            Style::Standard => "Standard",
2598            Style::Graphical => "Graphical",
2599        };
2600        write!(f, "{text}")
2601    }
2602}
2603
2604/// Determines the fill style of the control for drawing purposes.
2605/// This is used with the `Form` and `PictureBox` controls.
2606#[derive(
2607    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2608)]
2609#[repr(i32)]
2610pub enum FillStyle {
2611    /// The background is filled with a solid color.
2612    Solid = 0,
2613    /// The background is not filled.
2614    ///
2615    /// This is the default setting.
2616    #[default]
2617    Transparent = 1,
2618    /// The background is filled with a horizontal line pattern.
2619    HorizontalLine = 2,
2620    /// The background is filled with a vertical line pattern.
2621    VerticalLine = 3,
2622    /// The background is filled with a diagonal line pattern.
2623    UpwardDiagonal = 4,
2624    /// The background is filled with a diagonal line pattern that goes from the bottom left to the top right.
2625    /// This is the same as `UpwardDiagonal` but rotated 90 degrees.
2626    DownwardDiagonal = 5,
2627    /// The background is filled with a cross-hatch pattern.
2628    Cross = 6,
2629    /// The background is filled with a diagonal cross-hatch pattern.
2630    /// This is the same as `Cross` but rotated 45 degrees.
2631    DiagonalCross = 7,
2632}
2633
2634impl TryFrom<&str> for FillStyle {
2635    type Error = ErrorKind;
2636
2637    fn try_from(value: &str) -> Result<Self, Self::Error> {
2638        match value {
2639            "0" => Ok(FillStyle::Solid),
2640            "1" => Ok(FillStyle::Transparent),
2641            "2" => Ok(FillStyle::HorizontalLine),
2642            "3" => Ok(FillStyle::VerticalLine),
2643            "4" => Ok(FillStyle::UpwardDiagonal),
2644            "5" => Ok(FillStyle::DownwardDiagonal),
2645            "6" => Ok(FillStyle::Cross),
2646            "7" => Ok(FillStyle::DiagonalCross),
2647            _ => Err(ErrorKind::Form(FormError::InvalidFillStyle {
2648                value: value.to_string(),
2649            })),
2650        }
2651    }
2652}
2653
2654impl FromStr for FillStyle {
2655    type Err = ErrorKind;
2656
2657    fn from_str(s: &str) -> Result<Self, Self::Err> {
2658        FillStyle::try_from(s)
2659    }
2660}
2661
2662impl Display for FillStyle {
2663    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2664        let text = match self {
2665            FillStyle::Solid => "Solid",
2666            FillStyle::Transparent => "Transparent",
2667            FillStyle::HorizontalLine => "HorizontalLine",
2668            FillStyle::VerticalLine => "VerticalLine",
2669            FillStyle::UpwardDiagonal => "UpwardDiagonal",
2670            FillStyle::DownwardDiagonal => "DownwardDiagonal",
2671            FillStyle::Cross => "Cross",
2672            FillStyle::DiagonalCross => "DiagonalCross",
2673        };
2674        write!(f, "{text}")
2675    }
2676}
2677
2678/// Determines the link mode of a control for DDE conversations.
2679/// This is used with the `Form` control.
2680///
2681/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa235154(v=vs.60))
2682#[derive(
2683    Debug, PartialEq, Eq, Clone, Serialize, TryFromPrimitive, Default, Copy, Hash, PartialOrd, Ord,
2684)]
2685#[repr(i32)]
2686pub enum LinkMode {
2687    /// No DDE interaction. No destination application can initiate a conversation
2688    /// with the source control as the topic, and no application can poke data to
2689    /// the control.
2690    #[default]
2691    None = 0,
2692    /// Allows any `Label`, `PictureBox`, or `TextBox` control on a form to supply
2693    /// data to any destination application that establishes a DDE conversation
2694    /// with the control. If such a link exists, Visual Basic automatically
2695    /// notifies the destination whenever the contents of a control are changed.
2696    /// In addition, a destination application can poke data to any `Label`,
2697    /// `PictureBox`, or `TextBox` control on the form.
2698    Automatic = 1,
2699    /// Allows any `Label`, `PictureBox`, or `TextBox` control on a form to supply
2700    /// data to any destination application that establishes a DDE conversation
2701    /// with the control. However, Visual Basic does not automatically notify
2702    /// the destination whenever the contents of a control are changed. In
2703    /// addition, a destination application can poke data to any `Label`,
2704    /// `PictureBox`, or `TextBox` control on the form.
2705    Manual = 2,
2706    /// Allows any `Label`, `PictureBox`, or `TextBox` control on a form to supply
2707    /// data to any destination application that establishes a DDE conversation
2708    /// with the control. Visual Basic automatically notifies the destination
2709    /// whenever the contents of a control are changed. However, a destination
2710    /// application cannot poke data to any `Label`, `PictureBox`, or `TextBox`
2711    /// control on the form.
2712    Notify = 3,
2713}
2714
2715impl TryFrom<&str> for LinkMode {
2716    type Error = ErrorKind;
2717
2718    fn try_from(value: &str) -> Result<Self, Self::Error> {
2719        match value {
2720            "0" => Ok(LinkMode::None),
2721            "1" => Ok(LinkMode::Automatic),
2722            "2" => Ok(LinkMode::Manual),
2723            "3" => Ok(LinkMode::Notify),
2724            _ => Err(ErrorKind::Form(FormError::InvalidLinkMode {
2725                value: value.to_string(),
2726            })),
2727        }
2728    }
2729}
2730
2731impl FromStr for LinkMode {
2732    type Err = ErrorKind;
2733
2734    fn from_str(s: &str) -> Result<Self, Self::Err> {
2735        LinkMode::try_from(s)
2736    }
2737}
2738
2739impl Display for LinkMode {
2740    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2741        let text = match self {
2742            LinkMode::None => "None",
2743            LinkMode::Automatic => "Automatic",
2744            LinkMode::Manual => "Manual",
2745            LinkMode::Notify => "Notify",
2746        };
2747        write!(f, "{text}")
2748    }
2749}
2750
2751/// Determines the multi-select behavior of a `ListBox` control.
2752///
2753/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa235198(v=vs.60))
2754#[derive(
2755    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2756)]
2757#[repr(i32)]
2758pub enum MultiSelect {
2759    /// The user cannot select more than one item in the list box.
2760    #[default]
2761    None = 0,
2762    /// The user can select multiple items in the list box by holding down the
2763    /// `SHIFT` key while clicking items.
2764    Simple = 1,
2765    /// The user can select multiple items in the list box by holding down the
2766    /// `CTRL` key while clicking items.
2767    Extended = 2,
2768}
2769
2770impl TryFrom<&str> for MultiSelect {
2771    type Error = ErrorKind;
2772
2773    fn try_from(value: &str) -> Result<Self, Self::Error> {
2774        match value {
2775            "0" => Ok(MultiSelect::None),
2776            "1" => Ok(MultiSelect::Simple),
2777            "2" => Ok(MultiSelect::Extended),
2778            _ => Err(ErrorKind::Form(FormError::InvalidMultiSelect {
2779                value: value.to_string(),
2780            })),
2781        }
2782    }
2783}
2784
2785impl FromStr for MultiSelect {
2786    type Err = ErrorKind;
2787
2788    fn from_str(s: &str) -> Result<Self, Self::Err> {
2789        MultiSelect::try_from(s)
2790    }
2791}
2792
2793impl Display for MultiSelect {
2794    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2795        let text = match self {
2796            MultiSelect::None => "None",
2797            MultiSelect::Simple => "Simple",
2798            MultiSelect::Extended => "Extended",
2799        };
2800        write!(f, "{text}")
2801    }
2802}
2803
2804/// Determines the scale mode of the control for sizing and positioning.
2805/// This is used with the `Form` and `PictureBox` controls.
2806///
2807/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa445668(v=vs.60))
2808#[derive(
2809    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2810)]
2811#[repr(i32)]
2812pub enum ScaleMode {
2813    /// Indicates that one or more of the `ScaleHeight`, `ScaleWidth`, `ScaleLeft`, and `ScaleTop` properties are set to custom values.
2814    User = 0,
2815    /// The control uses twips as the unit of measurement. (1440 twips per logical inch; 567 twips per logical centimeter).
2816    #[default]
2817    Twip = 1,
2818    /// The control uses Points as the unit of measurement. (72 points per logical inch).
2819    Point = 2,
2820    /// The control uses Pixels as the unit of measurement. (The number of pixels per logical inch depends on the system's display settings).
2821    Pixel = 3,
2822    /// The control uses Characters as the unit of measurement. Character (horizontal = 120 twips per unit; vertical = 240 twips per unit).
2823    Character = 4,
2824    /// The control uses Inches as the unit of measurement.
2825    Inches = 5,
2826    /// The control uses Millimeters as the unit of measurement.
2827    Millimeter = 6,
2828    /// The control uses Centimeters as the unit of measurement.
2829    Centimeter = 7,
2830    /// The control uses `HiMetrics` as the unit of measurement.
2831    HiMetric = 8,
2832    /// The control uses the Units used by the control's container to determine the control's position.
2833    ContainerPosition = 9,
2834    /// The control uses the Units used by the control's container to determine the control's size.
2835    ContainerSize = 10,
2836}
2837
2838impl FromStr for ScaleMode {
2839    type Err = ErrorKind;
2840
2841    fn from_str(s: &str) -> Result<Self, Self::Err> {
2842        ScaleMode::try_from(s)
2843    }
2844}
2845
2846impl TryFrom<&str> for ScaleMode {
2847    type Error = ErrorKind;
2848
2849    fn try_from(value: &str) -> Result<Self, Self::Error> {
2850        match value {
2851            "0" => Ok(ScaleMode::User),
2852            "1" => Ok(ScaleMode::Twip),
2853            "2" => Ok(ScaleMode::Point),
2854            "3" => Ok(ScaleMode::Pixel),
2855            "4" => Ok(ScaleMode::Character),
2856            "5" => Ok(ScaleMode::Inches),
2857            "6" => Ok(ScaleMode::Millimeter),
2858            "7" => Ok(ScaleMode::Centimeter),
2859            "8" => Ok(ScaleMode::HiMetric),
2860            "9" => Ok(ScaleMode::ContainerPosition),
2861            "10" => Ok(ScaleMode::ContainerSize),
2862            _ => Err(ErrorKind::Form(FormError::InvalidScaleMode {
2863                value: value.to_string(),
2864            })),
2865        }
2866    }
2867}
2868
2869impl Display for ScaleMode {
2870    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2871        let text = match self {
2872            ScaleMode::User => "User",
2873            ScaleMode::Twip => "Twip",
2874            ScaleMode::Point => "Point",
2875            ScaleMode::Pixel => "Pixel",
2876            ScaleMode::Character => "Character",
2877            ScaleMode::Inches => "Inches",
2878            ScaleMode::Millimeter => "Millimeter",
2879            ScaleMode::Centimeter => "Centimeter",
2880            ScaleMode::HiMetric => "HiMetric",
2881            ScaleMode::ContainerPosition => "ContainerPosition",
2882            ScaleMode::ContainerSize => "ContainerSize",
2883        };
2884        write!(f, "{text}")
2885    }
2886}
2887
2888/// Determines how the control sizes the picture within its bounds.
2889/// This is used with the `Image` and `PictureBox` controls.
2890///
2891/// [Reference](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-basic-6/aa445695(v=vs.60))
2892#[derive(
2893    Debug, PartialEq, Eq, Clone, Serialize, Default, TryFromPrimitive, Copy, Hash, PartialOrd, Ord,
2894)]
2895#[repr(i32)]
2896pub enum SizeMode {
2897    /// The picture is displayed in its original size. If the picture is larger than
2898    /// the control, the picture is clipped to fit within the control's bounds.
2899    ///
2900    /// If the picture is smaller than the control, the picture is displayed in the
2901    /// top-left corner of the control, and the remaining area of the control is
2902    /// left blank.
2903    #[default]
2904    Clip = 0,
2905    /// The picture is stretched or shrunk to fit the control's bounds.
2906    Stretch = 1,
2907    /// The control is automatically resized to fit the picture.
2908    AutoSize = 2,
2909    /// The picture is stretched or shrunk to fit the control's bounds while maintaining its aspect ratio.
2910    Zoom = 3,
2911}
2912
2913impl TryFrom<&str> for SizeMode {
2914    type Error = ErrorKind;
2915
2916    fn try_from(value: &str) -> Result<Self, Self::Error> {
2917        match value {
2918            "0" => Ok(SizeMode::Clip),
2919            "1" => Ok(SizeMode::Stretch),
2920            "2" => Ok(SizeMode::AutoSize),
2921            "3" => Ok(SizeMode::Zoom),
2922            _ => Err(ErrorKind::Form(FormError::InvalidSizeMode {
2923                value: value.to_string(),
2924            })),
2925        }
2926    }
2927}
2928
2929impl FromStr for SizeMode {
2930    type Err = ErrorKind;
2931
2932    fn from_str(s: &str) -> Result<Self, Self::Err> {
2933        SizeMode::try_from(s)
2934    }
2935}
2936
2937impl Display for SizeMode {
2938    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
2939        let text = match self {
2940            SizeMode::Clip => "Clip",
2941            SizeMode::Stretch => "Stretch",
2942            SizeMode::AutoSize => "AutoSize",
2943            SizeMode::Zoom => "Zoom",
2944        };
2945        write!(f, "{text}")
2946    }
2947}