tiger_lib/gui/
properties.rs

1use std::fmt::{Display, Error, Formatter};
2use std::hash::Hash;
3
4use strum::VariantNames;
5use strum_macros::{Display, FromRepr, IntoStaticStr, VariantNames};
6use thiserror::Error;
7
8#[cfg(doc)]
9use crate::datatype::Datatype;
10use crate::game::GameFlags;
11use crate::gui::BuiltinWidget;
12#[cfg(doc)]
13use crate::gui::GuiBlock;
14use crate::item::Item;
15use crate::lowercase::Lowercase;
16
17use GuiValidation::*;
18use WidgetProperty::*;
19
20/// The various values or blocks or datatype calculations that the gui properties can take.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub enum GuiValidation {
23    /// Accept any value; we don't know.
24    UncheckedValue,
25    /// A datatype expression; we don't know the specific type.
26    DatatypeExpr,
27    /// A datatype expression that ends with a promote. Can be any type.
28    // TODO: check that it is a singular type, not a collection
29    Datacontext,
30    /// A datatype expression that returns a collection of some sort.
31    // TODO: check that it is indeed a collection
32    Datamodel,
33    /// "yes", "no", or a [`Datatype::bool`] expression.
34    Boolean,
35    /// Only a literal "yes" will do.
36    Yes,
37    /// A `|`-separated list of values from [`ALIGN`].
38    Align,
39    /// An integer value or a [`Datatype::int32`] expression.
40    Integer,
41    /// A non-negative integer value or a [`Datatype::uint32`] expression.
42    UnsignedInteger,
43    /// A numeric value or a [`Datatype::float`] expression.
44    Number,
45    /// A numeric value or a [`Datatype::float`] or [`Datatype::int32`] expression.
46    NumberOrInt32,
47    /// A numeric value possibly followed by an `f` or a [`Datatype::float`] expression.
48    // TODO: the f is used in vanilla. Check if it's harmless or maybe required.
49    NumberF,
50    /// A numeric value, possibly followed by a `%` mark, or a [`Datatype::int32`] or
51    /// [`Datatype::float`] expression.
52    NumberOrPercent,
53    /// A block with two numeric values, possibly followed by `%` marks, or a
54    /// [`Datatype::CVector2f`] expression.
55    TwoNumberOrPercent,
56    /// A numeric value or a numberlike datatype (one of the floats, ints, or `CFixedPoint`)
57    Numeric,
58    /// A block with 2 numbers, or a [`Datatype::CVector2f`] expression.
59    CVector2f,
60    /// A block with 2 integers, or a [`Datatype::CVector2i`] expression.
61    CVector2i,
62    /// A block with 3 numbers, or a [`Datatype::CVector3f`] expression.
63    CVector3f,
64    /// A block with 4 numbers, or a [`Datatype::CVector4f`] expression.
65    CVector4f,
66    /// A block with a series of blocks with 2 numbers, or a datatype expression.
67    PointsList,
68    /// A block with 4 numbers (RGBA), or a [`Datatype::CVector4f`] expression.
69    /// The difference with [`GuiValidation::CVector4f`] is in the error messages.
70    Color,
71    /// A datatype expression returning a `CString`
72    CString,
73    /// A key for this item type, or a datatype expression that can be converted to string.
74    Item(Item),
75    /// A key for this item type, or a datatype expression that can be converted to string.
76    /// May be the empty string.
77    ItemOrBlank(Item),
78    /// One of the strings in the [`BLENDMODES`] array
79    Blendmode,
80    /// One of the mouse buttons in the array given here.
81    MouseButton(&'static [&'static str]),
82    /// A `|`-separated list of the mouse buttons in the array given here.
83    MouseButtonSet(&'static [&'static str]),
84    /// One of the strings in the array given here.
85    Choice(&'static [&'static str]),
86    /// A `|`-separated list of strings from the array given here.
87    ChoiceSet(&'static [&'static str]),
88    /// A block containing one widget, or the name of a template containing one widget.
89    Widget,
90    /// An action tooltip block.
91    ActionTooltip,
92    /// A string containing a localization text format specifier starting with `#`
93    Format,
94    /// A block containing two textformat names
95    FormatOverride,
96    /// Some text. May contain datatypes or loca keys, but it's optional.
97    RawText,
98    /// Some text. May contain datatypes or loca keys.
99    Text,
100    /// A property that takes a block of other properties. This block may have override blocks in it.
101    ComplexProperty,
102}
103
104/// All the properties that can be used in gui widgets.
105// These need to be in lexical order, for the `TryFrom` implementation to work right.
106#[derive(
107    Debug, Clone, Copy, PartialEq, Eq, Hash, IntoStaticStr, VariantNames, FromRepr, Display,
108)]
109#[allow(non_camel_case_types)]
110#[strum(serialize_all = "lowercase")] // for "loop"
111pub enum WidgetProperty {
112    accept_tabs,
113    acceptance,
114    action_tooltip,
115    active_item,
116    activeframe,
117    actor,
118    addcolumn,
119    addrow,
120    align,
121    allow_outside,
122    alpha,
123    alwaystransparent,
124    animate_negative_changes,
125    animation,
126    animation_speed,
127    attachto,
128    autoresize,
129    autoresize_axis,
130    autoresize_slider,
131    autoresizescrollarea,
132    autoresizeviewport,
133    axis_label,
134    background_texture,
135    bezier,
136    blend_mode,
137    bottomtotop,
138    button_ignore,
139    button_tooltip_override,
140    button_trigger,
141    buttontext,
142    camera_fov_y_degrees,
143    camera_look_at,
144    camera_near_far,
145    camera_position,
146    camera_rotation_pitch_limits,
147    camera_translation_limits,
148    camera_zoom_limits,
149    cameracontrolwidget_entity_view,
150    checked,
151    clamp_pan_position,
152    clear_color,
153    click_mode,
154    click_modifier,
155    click_modifiers,
156    click_type,
157    clicksound,
158    clicksoundlater,
159    close_on_click_outside,
160    close_sound,
161    coat_of_arms,
162    coat_of_arms_mask,
163    coat_of_arms_offset,
164    coat_of_arms_scale,
165    coat_of_arms_slot,
166    color,
167    colormap_coordinates,
168    colorpicker_reticule_icon,
169    conditions,
170    confirmation,
171    constantbuffers,
172    contextmenu_enabled,
173    contextmenu_widget,
174    cost,
175    cursor,
176    cursorcolor,
177    datacontext,
178    datamodel,
179    datamodel_reuse_widgets,
180    datamodel_wrap,
181    debug_text,
182    dec_button,
183    default_clicksound,
184    default_format,
185    delay,
186    description,
187    direction,
188    disable_common_context,
189    disable_input_fallthrough,
190    disableframe,
191    distance,
192    distribute_visual_state,
193    down,
194    downframe,
195    downhoverframe,
196    downpressedframe,
197    drag_drop_args,
198    drag_drop_base_type,
199    drag_drop_data,
200    drag_drop_id,
201    dragdropargs,
202    dragdropid,
203    draggable_behavior,
204    draggable_by,
205    drop_target,
206    droptarget,
207    duration,
208    effect,
209    effectname,
210    effects,
211    elide,
212    enabled,
213    enabled_input,
214    enabled_post_effects,
215    end_sound,
216    endangle,
217    entity_enable_sound,
218    entity_instance,
219    even_row_widget,
220    expand_item,
221    expandbutton,
222    fade_from_point,
223    fade_to_point,
224    filter_mouse,
225    fittype,
226    flipdirection,
227    focus_on_visible,
228    focuspolicy,
229    font,
230    fontcolor,
231    fontsize,
232    fontsize_min,
233    fonttintcolor,
234    fontweight,
235    force_data_properties_update,
236    forcedown,
237    format_override,
238    frame,
239    frame_grid,
240    frame_tier,
241    framesize,
242    from,
243    gfx_environment_file,
244    gfxtype,
245    glow,
246    glow_alpha,
247    glow_alpha_mask,
248    glow_blur_passes,
249    glow_generation_rules,
250    glow_ignore_inside_pixels,
251    glow_radius,
252    glow_texture_downscale,
253    grayscale,
254    grid_entity_name,
255    header_height,
256    highlightchecked,
257    hold_time_factor,
258    ignore_in_debug_draw,
259    ignore_layout,
260    ignore_unset_buttons,
261    ignoreinvisible,
262    inc_button,
263    indent,
264    index,
265    inherit_data_context,
266    inherit_visibility,
267    inherit_visual_state,
268    input_action,
269    input_context,
270    intersectionmask,
271    intersectionmask_texture,
272    invert_reticule_color,
273    invertprogress,
274    is_local_scale_fixed,
275    item,
276    keyframe_editor_lane_container,
277    layer,
278    layoutanchor,
279    layoutpolicy_horizontal,
280    layoutpolicy_vertical,
281    layoutstretchfactor_horizontal,
282    layoutstretchfactor_vertical,
283    left_action,
284    left_click_and_hold_action,
285    line_cap,
286    line_feather_distance,
287    line_type,
288    list,
289    Loop, // titlecased to avoid collision with builtin loop
290    loopinterval,
291    lowpriotextcontext,
292    margin,
293    margin_bottom,
294    margin_left,
295    margin_right,
296    margin_top,
297    marker,
298    mask,
299    mask_uv_scale,
300    max,
301    max_aspect_ratio,
302    max_height,
303    max_update_rate,
304    max_width,
305    maxcharacters,
306    maxhorizontalslots,
307    maximumsize,
308    maxverticalslots,
309    min,
310    min_dist_from_screen_edge,
311    min_height,
312    min_width,
313    minimumsize,
314    mipmaplodbias,
315    mirror,
316    modal,
317    modality,
318    modify_texture,
319    movable,
320    multiline,
321    name,
322    next,
323    noprogresstexture,
324    odd_row_widget,
325    on_action,
326    on_action_with_params,
327    on_escape_pressed,
328    on_finish,
329    on_input_action_shortcut,
330    on_keyframe_move,
331    on_start,
332    onalt,
333    onchangefinish,
334    onchangestart,
335    onclick,
336    oncolorchanged,
337    oncoloredited,
338    oncreate,
339    onctrl,
340    ondatacontextchanged,
341    ondefault,
342    ondoubleclick,
343    oneditingfinished,
344    oneditingfinished_with_changes,
345    oneditingstart,
346    onenter_signal,
347    onfocusout,
348    onleave_signal,
349    onmousedragfinished,
350    onmousedragged,
351    onmousedragstarted,
352    onmousehierarchyenter,
353    onmousehierarchyleave,
354    onpressed,
355    onreleased,
356    onreturnpressed,
357    onrightclick,
358    onrightpressed,
359    onrightreleased,
360    onselectionchanged,
361    onshift,
362    ontextchanged,
363    ontextcontextchanged,
364    ontextedited,
365    onvaluechanged,
366    open_sound,
367    overframe,
368    oversound,
369    page,
370    pan_position,
371    parameter,
372    params,
373    parentanchor,
374    password,
375    plotpoints,
376    plotrect,
377    points,
378    pop_out,
379    pop_out_v,
380    portrait_context,
381    portrait_offset,
382    portrait_scale,
383    portrait_texture,
384    position,
385    position_x,
386    position_y,
387    preferscrollwidgetsize,
388    primary_color,
389    progress_change_to_duration_curve,
390    progresstexture,
391    proposer,
392    pseudo_localization_enabled,
393    ranged_slider,
394    raw_text,
395    raw_tooltip,
396    realtime,
397    recursive,
398    render_pass,
399    reorder_on_mouse,
400    resizable,
401    resizeparent,
402    resizetofit,
403    restart_on_show,
404    restrictparent_min,
405    reuse_widgets,
406    right_action,
407    right_click_and_hold_action,
408    rightclick_modifiers,
409    rightclicksound,
410    righttoleft,
411    rotate_uv,
412    row_height,
413    scale,
414    scale_mode,
415    scene,
416    scissor,
417    screen_grab,
418    scrollbar_horizontal,
419    scrollbar_vertical,
420    scrollbaralign_horizontal,
421    scrollbaralign_vertical,
422    scrollbarpolicy_horizontal,
423    scrollbarpolicy_vertical,
424    scrollwidget,
425    secondary_color,
426    selectallonfocus,
427    selectedindex,
428    selectioncolor,
429    set_parent_dimension_to_minimum,
430    set_parent_size_to_minimum,
431    setitemsizefromcell,
432    shaderfile,
433    shortcut,
434    size,
435    skip_initial_animation,
436    slider,
437    snap_to_pixels,
438    soundeffect,
439    soundparam,
440    spacing,
441    speed,
442    spriteborder,
443    spriteborder_bottom,
444    spriteborder_left,
445    spriteborder_right,
446    spriteborder_top,
447    spritetype,
448    stackmode,
449    start_sound,
450    startangle,
451    state,
452    step,
453    sticky,
454    tabfocusroot,
455    tagtooltip_enabled,
456    tertiary_color,
457    text,
458    text_selectable,
459    text_validator,
460    textcontext,
461    texture,
462    texture_density,
463    timeline_line_direction,
464    timeline_line_height,
465    timeline_texts,
466    timeline_time_points,
467    tintcolor,
468    title,
469    to,
470    tooltip,
471    tooltip_enabled,
472    tooltip_horizontalbehavior,
473    tooltip_offset,
474    tooltip_parentanchor,
475    tooltip_type,
476    tooltip_verticalbehavior,
477    tooltip_visible,
478    tooltip_when_disabled,
479    tooltip_widgetanchor,
480    tooltipmeta,
481    tooltipwidget,
482    track,
483    track_highlight,
484    track_range,
485    tracknavigation,
486    translate_uv,
487    trigger_on_create,
488    trigger_when,
489    upframe,
490    uphoverframe,
491    uppressedframe,
492    url,
493    use_for_loading_screen,
494    use_global_input_instance,
495    useragent,
496    uv_scale,
497    value,
498    video,
499    viewportwidget,
500    visible,
501    visible_at_creation,
502    wheelstep,
503    widgetanchor,
504    widgetid,
505    width,
506    wrap_count,
507    wrap_length,
508    zoom,
509    zoom_max,
510    zoom_min,
511    zoom_speed,
512    zoom_step,
513    zoomwidget,
514}
515
516#[derive(Error, Debug)]
517pub enum TryWidgetPropertyError {
518    #[error("widget property `{0}` not found")]
519    NotFound(String),
520}
521
522impl<'a> TryFrom<&Lowercase<'a>> for WidgetProperty {
523    type Error = TryWidgetPropertyError;
524
525    fn try_from(s: &Lowercase<'a>) -> Result<Self, Self::Error> {
526        match WidgetProperty::VARIANTS.binary_search(&s.as_str()) {
527            // unwrap is safe here because of how VARIANTS is constructed
528            Ok(i) => Ok(WidgetProperty::from_repr(i).unwrap()),
529            Err(_) => Err(TryWidgetPropertyError::NotFound(s.to_string())),
530        }
531    }
532}
533
534const LAYOUT_POLICIES: &[&str] = &["expanding", "fixed", "growing", "preferred", "shrinking"];
535
536pub const BLENDMODES: &[&str] = &[
537    "add",
538    "alphamultiply",
539    "colordodge",
540    "darken",
541    "mask",
542    "multiply",
543    "normal",
544    "overlay",
545    "lighten",
546];
547
548// TODO: warn about contradicting alignments (left|right or top|vcenter)
549// TODO: is nobaseline only for text widgets?
550pub const ALIGN: &[&str] =
551    &["left", "right", "top", "bottom", "center", "hcenter", "vcenter", "nobaseline"];
552
553impl GuiValidation {
554    /// Get the validation logic for a specific widget property
555    pub fn from_property(property: WidgetProperty) -> Self {
556        #[allow(clippy::match_same_arms)] // keep it alphabetic
557        match property {
558            accept_tabs => Boolean,
559            acceptance => DatatypeExpr,
560            action_tooltip => ActionTooltip,
561            active_item => Widget,
562            activeframe => Integer,
563            actor => Datacontext,
564            addcolumn => NumberOrPercent,
565            addrow => NumberOrPercent,
566            align => Align,
567            allow_outside => Boolean,
568            alpha => Number,
569            alwaystransparent => Boolean,
570            animate_negative_changes => Boolean,
571            animation => ComplexProperty,
572            animation_speed => CVector2f,
573            attachto => ComplexProperty,
574            autoresize => Boolean,
575            autoresize_axis => Choice(&["horizontal", "vertical"]),
576            autoresize_slider => Boolean,
577            autoresizescrollarea => Boolean,
578            autoresizeviewport => Boolean,
579            axis_label => Widget,
580            background_texture => Item(Item::File),
581            bezier => CVector4f,
582            blend_mode => Blendmode,
583            bottomtotop => Boolean,
584            button_ignore => MouseButton(&["both", "none", "left", "right"]),
585            button_tooltip_override => UncheckedValue, // TODO
586            button_trigger => UncheckedValue,          // only example is "none"
587            buttontext => Widget,
588            camera_fov_y_degrees => Integer,
589            camera_look_at => CVector3f,
590            camera_near_far => CVector2f,
591            camera_position => CVector3f,
592            camera_rotation_pitch_limits => CVector2f,
593            camera_translation_limits => CVector3f,
594            camera_zoom_limits => CVector2f,
595            cameracontrolwidget_entity_view => Widget,
596            checked => Boolean,
597            clamp_pan_position => Boolean,
598            clear_color => CVector4f,
599            click_mode => UncheckedValue,     // TODO
600            click_modifier => UncheckedValue, // TODO
601            click_modifiers => ComplexProperty,
602            click_type => UncheckedValue, // TODO
603            clicksound => ItemOrBlank(Item::Sound),
604            clicksoundlater => UncheckedValue, // TODO
605            close_on_click_outside => Boolean,
606            close_sound => Item(Item::Sound),
607            coat_of_arms => Item(Item::File),
608            coat_of_arms_mask => Item(Item::File),
609            coat_of_arms_offset => CVector2f,
610            coat_of_arms_scale => CVector2f,
611            coat_of_arms_slot => CVector4f,
612            color => Color,
613            colormap_coordinates => UncheckedValue, // TODO
614            colorpicker_reticule_icon => Widget,
615            conditions => UncheckedValue,   // TODO
616            confirmation => UncheckedValue, // TODO
617            constantbuffers => DatatypeExpr,
618            contextmenu_enabled => UncheckedValue, // TODO
619            contextmenu_widget => UncheckedValue,  // TODO
620            cost => UncheckedValue,                // TODO
621            cursor => UncheckedValue,              // TODO
622            cursorcolor => Color,
623            datacontext => Datacontext,
624            datamodel => Datamodel,
625            datamodel_reuse_widgets => Boolean,
626            datamodel_wrap => Integer,
627            debug_text => UncheckedValue, // TODO
628            dec_button => Widget,
629            default_clicksound => ItemOrBlank(Item::Sound),
630            default_format => Format,
631            delay => Number,
632            description => UncheckedValue, // TODO
633            direction => Choice(&["horizontal", "vertical"]),
634            disable_common_context => UncheckedValue, // TODO
635            disable_input_fallthrough => UncheckedValue, // TODO
636            disableframe => Integer,
637            distance => Integer,
638            distribute_visual_state => Boolean,
639            down => Boolean,
640            downframe => Integer,
641            downhoverframe => Integer,
642            downpressedframe => Integer,
643            drag_drop_args => CString,
644            drag_drop_base_type => Choice(&["icon", "coat_of_arms_icon"]),
645            drag_drop_data => Datacontext,
646            drag_drop_id => UncheckedValue, // TODO what are the options?
647            dragdropargs => RawText,
648            dragdropid => RawText,
649            draggable_behavior => UncheckedValue, // TODO
650            draggable_by => MouseButtonSet(&["left", "right", "middle"]),
651            drop_target => UncheckedValue, // TODO
652            droptarget => Boolean,
653            duration => Number,
654            effect => DatatypeExpr,
655            effectname => UncheckedValue, // TODO validate effect names
656            effects => UncheckedValue,    // TODO
657            elide => Choice(&["right", "middle", "left"]),
658            enabled => Boolean,
659            enabled_input => UncheckedValue, // TODO
660            enabled_post_effects => UncheckedValue,
661            end_sound => ComplexProperty,
662            endangle => NumberOrInt32,
663            entity_enable_sound => Boolean,
664            entity_instance => Item(Item::Entity),
665            even_row_widget => Widget,
666            expand_item => Widget,
667            expandbutton => Widget,
668            fade_from_point => CVector2f,
669            fade_to_point => CVector2f,
670            filter_mouse => MouseButtonSet(&["all", "none", "left", "right", "wheel"]),
671            fittype => Choice(&["center", "centercrop", "fill", "end", "start"]),
672            flipdirection => Boolean,
673            focus_on_visible => Boolean,
674            focuspolicy => Choice(&["click", "all", "none"]),
675            font => Item(Item::Font),
676            fontcolor => Color,
677            fontsize => Integer,
678            fontsize_min => Integer,
679            fonttintcolor => Color,
680            fontweight => UncheckedValue, // TODO: what are the options?
681            force_data_properties_update => Boolean,
682            forcedown => DatatypeExpr,
683            format_override => FormatOverride,
684            frame => Integer,
685            frame_grid => CVector2i,
686            frame_tier => Integer,
687            framesize => CVector2i,
688            from => CVector2f,
689            gfx_environment_file => Item(Item::File),
690            gfxtype => UncheckedValue, // TODO: what are the options?
691            glow => ComplexProperty,
692            glow_alpha => Number,
693            glow_alpha_mask => Integer,
694            glow_blur_passes => Integer,
695            glow_generation_rules => ComplexProperty,
696            glow_ignore_inside_pixels => Boolean,
697            glow_radius => Integer,
698            glow_texture_downscale => NumberF,
699            grayscale => Boolean,
700            grid_entity_name => Item(Item::Entity),
701            header_height => Integer,
702            highlightchecked => Boolean,
703            hold_time_factor => UncheckedValue, // TODO
704            ignore_in_debug_draw => Boolean,
705            ignore_layout => UncheckedValue, // TODO
706            ignore_unset_buttons => MouseButtonSet(&["right", "middle", "left"]), // middle and left are guesses
707            ignoreinvisible => Boolean,
708            inc_button => Widget,
709            indent => Integer,
710            index => Integer,
711            inherit_data_context => Boolean,
712            inherit_visibility => Choice(&["yes", "no", "hidden"]),
713            inherit_visual_state => Boolean,
714            input_action => Item(Item::Shortcut),
715            input_context => UncheckedValue, // TODO: what are the options?
716            intersectionmask => Boolean,
717            intersectionmask_texture => Item(Item::File),
718            invert_reticule_color => Boolean,
719            invertprogress => Boolean,
720            is_local_scale_fixed => Boolean,
721            item => Widget,
722            keyframe_editor_lane_container => Widget,
723            layer => Item(Item::GuiLayer),
724            layoutanchor => UncheckedValue, // TODO: only example is "bottomleft"
725            layoutpolicy_horizontal => ChoiceSet(LAYOUT_POLICIES),
726            layoutpolicy_vertical => ChoiceSet(LAYOUT_POLICIES),
727            layoutstretchfactor_horizontal => NumberOrInt32,
728            layoutstretchfactor_vertical => NumberOrInt32,
729            left_action => UncheckedValue,                // TODO
730            left_click_and_hold_action => UncheckedValue, // TODO
731            line_cap => Boolean,
732            line_feather_distance => Integer,
733            line_type => UncheckedValue, // TODO: only example is "nodeline"
734            list => Widget,
735            Loop => Boolean,
736            loopinterval => Number,
737            lowpriotextcontext => UncheckedValue, // TODO
738            margin => TwoNumberOrPercent,
739            margin_bottom => NumberOrInt32,
740            margin_left => NumberOrInt32,
741            margin_right => NumberOrInt32,
742            margin_top => NumberOrInt32,
743            marker => Widget,
744            mask => Item(Item::File),
745            mask_uv_scale => CVector2f,
746            max => NumberOrInt32,
747            max_aspect_ratio => UncheckedValue, // TODO
748            max_height => Integer,
749            max_update_rate => Integer,
750            max_width => Integer,
751            maxcharacters => UnsignedInteger,
752            maxhorizontalslots => Integer,
753            maximumsize => TwoNumberOrPercent,
754            maxverticalslots => Integer,
755            min => NumberOrInt32,
756            min_dist_from_screen_edge => Integer,
757            min_height => Integer,
758            min_width => Integer,
759            minimumsize => TwoNumberOrPercent,
760            mipmaplodbias => Integer,
761            mirror => ChoiceSet(&["horizontal", "vertical"]),
762            modal => Boolean,
763            modality => UncheckedValue, // TODO: only example is "all"
764            modify_texture => ComplexProperty,
765            movable => Boolean,
766            multiline => Boolean,
767            name => UncheckedValue,
768            next => UncheckedValue, // TODO: choices are states in the same widget
769            noprogresstexture => Item(Item::File),
770            odd_row_widget => Widget,
771            on_action => UncheckedValue,             // TODO
772            on_action_with_params => UncheckedValue, // TODO
773            on_escape_pressed => UncheckedValue,     // TODO
774            on_finish => DatatypeExpr,
775            on_input_action_shortcut => UncheckedValue, // TODO
776            on_keyframe_move => DatatypeExpr,
777            on_start => DatatypeExpr,
778            onalt => DatatypeExpr,
779            onchangefinish => DatatypeExpr,
780            onchangestart => DatatypeExpr,
781            onclick => DatatypeExpr,
782            oncolorchanged => DatatypeExpr,
783            oncoloredited => DatatypeExpr,
784            oncreate => DatatypeExpr,
785            onctrl => DatatypeExpr,
786            ondatacontextchanged => DatatypeExpr,
787            ondefault => DatatypeExpr,
788            ondoubleclick => DatatypeExpr,
789            oneditingfinished => DatatypeExpr,
790            oneditingfinished_with_changes => DatatypeExpr,
791            oneditingstart => DatatypeExpr,
792            onenter_signal => DatatypeExpr,
793            onfocusout => DatatypeExpr,
794            onleave_signal => DatatypeExpr,
795            onmousedragfinished => UncheckedValue,
796            onmousedragged => UncheckedValue,
797            onmousedragstarted => UncheckedValue,
798            onmousehierarchyenter => DatatypeExpr,
799            onmousehierarchyleave => DatatypeExpr,
800            onpressed => DatatypeExpr,
801            onreleased => DatatypeExpr,
802            onreturnpressed => DatatypeExpr,
803            onrightclick => DatatypeExpr,
804            onrightpressed => UncheckedValue,  // TODO
805            onrightreleased => UncheckedValue, // TODO
806            onselectionchanged => DatatypeExpr,
807            onshift => DatatypeExpr,
808            ontextchanged => DatatypeExpr,
809            ontextcontextchanged => UncheckedValue, // TODO
810            ontextedited => DatatypeExpr,
811            onvaluechanged => DatatypeExpr,
812            open_sound => Item(Item::Sound),
813            overframe => Integer,
814            oversound => ItemOrBlank(Item::Sound),
815            page => Integer,
816            pan_position => CVector2f,
817            parameter => UncheckedValue, // TODO
818            params => UncheckedValue,    // TODO
819            parentanchor => Align,
820            password => Boolean,
821            plotpoints => DatatypeExpr,
822            plotrect => UncheckedValue, // TODO
823            points => PointsList,
824            pop_out => Boolean,
825            pop_out_v => NumberOrInt32,
826            portrait_context => DatatypeExpr,
827            portrait_offset => CVector2f,
828            portrait_scale => CVector2f,
829            portrait_texture => Item(Item::File),
830            position => TwoNumberOrPercent,
831            position_x => Numeric,
832            position_y => Numeric,
833            preferscrollwidgetsize => Boolean,
834            primary_color => UncheckedValue, // TODO
835            progress_change_to_duration_curve => CVector4f,
836            progresstexture => Item(Item::File),
837            proposer => UncheckedValue, // TODO
838            pseudo_localization_enabled => Boolean,
839            ranged_slider => UncheckedValue, // TODO
840            raw_text => RawText,
841            raw_tooltip => RawText,
842            realtime => Boolean,
843            recursive => Yes,
844            render_pass => UncheckedValue, // only example is an unknown datafunction
845            reorder_on_mouse => UncheckedValue, // TODO: only example is "presstop"
846            resizable => Boolean,
847            resizeparent => Boolean,
848            resizetofit => UncheckedValue, // TODO
849            restart_on_show => Boolean,
850            restrictparent_min => Boolean,
851            reuse_widgets => Boolean,
852            right_action => UncheckedValue,                // TODO
853            right_click_and_hold_action => UncheckedValue, // TODO
854            rightclick_modifiers => ComplexProperty,
855            rightclicksound => ItemOrBlank(Item::Sound),
856            righttoleft => Boolean,
857            rotate_uv => Number,
858            row_height => Integer,
859            scale => Number,
860            scale_mode => UncheckedValue, // TODO: only example is "fixedwidth"
861            scene => UncheckedValue,      // TODO
862            scissor => Boolean,
863            screen_grab => UncheckedValue, // TODO
864            scrollbar_horizontal => Widget,
865            scrollbar_vertical => Widget,
866            scrollbaralign_horizontal => Choice(&["top", "bottom", "noalign"]),
867            scrollbaralign_vertical => Choice(&["left", "right", "noalign"]),
868            scrollbarpolicy_horizontal => Choice(&["as_needed", "always_off", "always_on"]), // TODO: always_on is a guess
869            scrollbarpolicy_vertical => Choice(&["as_needed", "always_off", "always_on"]),
870            scrollwidget => Widget,
871            secondary_color => UncheckedValue, // TODO
872            selectallonfocus => Boolean,
873            selectedindex => CVector2i,
874            selectioncolor => Color,
875            set_parent_dimension_to_minimum => UncheckedValue, // TODO
876            set_parent_size_to_minimum => Boolean,
877            setitemsizefromcell => Boolean,
878            shaderfile => ItemOrBlank(Item::File),
879            shortcut => Item(Item::Shortcut),
880            size => TwoNumberOrPercent,
881            skip_initial_animation => Boolean,
882            slider => Widget,
883            snap_to_pixels => Boolean,
884            soundeffect => Item(Item::Sound),
885            soundparam => ComplexProperty,
886            spacing => NumberF,
887            speed => UncheckedValue, // TODO
888            spriteborder => CVector2f,
889            spriteborder_bottom => Integer,
890            spriteborder_left => Integer,
891            spriteborder_right => Integer,
892            spriteborder_top => Integer,
893            spritetype => UncheckedValue, // TODO
894            stackmode => UncheckedValue,  // TODO only example is "top"
895            start_sound => ComplexProperty,
896            startangle => NumberOrInt32,
897            state => ComplexProperty,
898            step => NumberOrInt32,
899            sticky => Boolean,
900            tabfocusroot => Boolean,
901            tagtooltip_enabled => UncheckedValue, // TODO
902            tertiary_color => UncheckedValue,     // TODO
903            text => Text,
904            text_selectable => Boolean,
905            text_validator => DatatypeExpr,
906            textcontext => UncheckedValue, // TODO
907            texture => Item(Item::File),
908            texture_density => Number,
909            timeline_line_direction => UncheckedValue, // TODO only example is "up"
910            timeline_line_height => Integer,
911            timeline_texts => Widget,
912            timeline_time_points => Integer,
913            tintcolor => Color,
914            title => UncheckedValue, // TODO
915            to => CVector2f,
916            tooltip => Text,
917            tooltip_enabled => Boolean,
918            tooltip_horizontalbehavior => Choice(&["mirror", "slide", "flip"]),
919            tooltip_offset => TwoNumberOrPercent,
920            tooltip_parentanchor => Align,
921            tooltip_type => Choice(&["mouse", "widget"]),
922            tooltip_verticalbehavior => Choice(&["mirror", "slide", "flip"]),
923            tooltip_visible => Boolean,
924            tooltip_when_disabled => Text,
925            tooltip_widgetanchor => Align,
926            tooltipmeta => ComplexProperty,
927            tooltipwidget => Widget,
928            track => Widget,
929            track_highlight => UncheckedValue, // TODO
930            track_range => UncheckedValue,     // TODO
931            tracknavigation => UncheckedValue, // TODO only example is "direct"
932            translate_uv => CVector2f,
933            trigger_on_create => Boolean,
934            trigger_when => Boolean,
935            upframe => Integer,
936            uphoverframe => Integer,
937            uppressedframe => Integer,
938            url => RawText,
939            use_for_loading_screen => UncheckedValue, // TODO
940            use_global_input_instance => UncheckedValue, // TODO
941            useragent => UncheckedValue,
942            uv_scale => CVector2f,
943            value => NumberOrInt32,
944            video => Item(Item::File),
945            viewportwidget => Widget,
946            visible => Boolean,
947            visible_at_creation => Boolean,
948            wheelstep => NumberOrInt32,
949            widgetanchor => Align,
950            widgetid => UncheckedValue,
951            width => Number,
952            wrap_count => Number,
953            wrap_length => UncheckedValue, // TODO
954            zoom => Number,
955            zoom_max => Number,
956            zoom_min => Number,
957            zoom_speed => UncheckedValue, // TODO
958            zoom_step => Number,
959            zoomwidget => Widget,
960        }
961    }
962}
963
964impl WidgetProperty {
965    /// Return which games support a given widget property
966    // LAST UPDATED CK3 VERSION 1.9.2.1
967    // LAST UPDATED VIC3 VERSION 1.3.6
968    // LAST UPDATED IMPERATOR VERSION 2.0.3
969    // TODO: verify rightclicksound property for Vic3 and Imperator
970    pub fn to_game_flags(self) -> GameFlags {
971        #[allow(clippy::match_same_arms)] // alphabetic is better
972        match self {
973            animate_negative_changes | autoresize_slider | click_modifiers => {
974                GameFlags::Ck3 | GameFlags::Vic3 | GameFlags::Eu5
975            }
976
977            coat_of_arms | coat_of_arms_mask => GameFlags::Ck3,
978
979            coat_of_arms_slot | colorpicker_reticule_icon => {
980                GameFlags::Ck3 | GameFlags::Vic3 | GameFlags::Eu5
981            }
982
983            drag_drop_args | drag_drop_base_type | drag_drop_id => GameFlags::Ck3,
984
985            drag_drop_data
986            | focus_on_visible
987            | force_data_properties_update
988            | grid_entity_name
989            | ignore_in_debug_draw
990            | ignore_unset_buttons
991            | index => GameFlags::Ck3 | GameFlags::Vic3 | GameFlags::Eu5,
992
993            input_action => GameFlags::Vic3 | GameFlags::Eu5,
994
995            invert_reticule_color | keyframe_editor_lane_container | Loop => {
996                GameFlags::Ck3 | GameFlags::Vic3 | GameFlags::Eu5
997            }
998
999            max_height => GameFlags::Ck3 | GameFlags::Eu5,
1000
1001            max_update_rate | min_dist_from_screen_edge => {
1002                GameFlags::Ck3 | GameFlags::Vic3 | GameFlags::Eu5
1003            }
1004
1005            min_height => GameFlags::Ck3 | GameFlags::Eu5,
1006
1007            on_keyframe_move
1008            | onalt
1009            | ondefault
1010            | ondoubleclick
1011            | oneditingfinished_with_changes
1012            | oneditingstart
1013            | onfocusout
1014            | onshift
1015            | progress_change_to_duration_curve
1016            | pseudo_localization_enabled
1017            | raw_text
1018            | raw_tooltip
1019            | restart_on_show
1020            | rightclick_modifiers
1021            | selectallonfocus
1022            | skip_initial_animation
1023            | timeline_line_direction
1024            | timeline_line_height
1025            | timeline_texts
1026            | timeline_time_points => GameFlags::Ck3 | GameFlags::Vic3 | GameFlags::Eu5,
1027
1028            action_tooltip
1029            | actor
1030            | autoresize_axis
1031            | button_tooltip_override
1032            | cameracontrolwidget_entity_view
1033            | clamp_pan_position
1034            | clear_color
1035            | colormap_coordinates
1036            | contextmenu_enabled
1037            | contextmenu_widget
1038            | debug_text
1039            | description
1040            | disable_common_context
1041            | disable_input_fallthrough
1042            | distance
1043            | draggable_behavior
1044            | drop_target
1045            | enabled_input
1046            | frame_grid
1047            | ignore_layout
1048            | left_action
1049            | left_click_and_hold_action
1050            | lowpriotextcontext
1051            | max_aspect_ratio
1052            | on_escape_pressed
1053            | on_input_action_shortcut
1054            | onrightpressed
1055            | onrightreleased
1056            | ontextcontextchanged
1057            | parameter
1058            | params
1059            | plotrect
1060            | primary_color
1061            | proposer
1062            | ranged_slider
1063            | resizetofit
1064            | right_action
1065            | right_click_and_hold_action
1066            | scene
1067            | screen_grab
1068            | secondary_color
1069            | set_parent_dimension_to_minimum
1070            | speed
1071            | tagtooltip_enabled
1072            | tertiary_color
1073            | textcontext
1074            | title
1075            | tooltipmeta
1076            | track_highlight
1077            | track_range
1078            | use_for_loading_screen
1079            | use_global_input_instance
1080            | wrap_length
1081            | zoom_speed => GameFlags::Eu5,
1082
1083            tooltip_enabled => GameFlags::Vic3 | GameFlags::Imperator | GameFlags::Eu5,
1084            tooltip_visible | tooltip_when_disabled => GameFlags::Ck3,
1085            video | wrap_count => GameFlags::Ck3 | GameFlags::Vic3 | GameFlags::Eu5,
1086
1087            onctrl => GameFlags::Vic3 | GameFlags::Eu5,
1088
1089            frame_tier | ondatacontextchanged | dragdropid | dragdropargs | forcedown | url
1090            | onleave_signal | onenter_signal => GameFlags::Imperator,
1091
1092            _ => GameFlags::all(),
1093        }
1094    }
1095}
1096
1097/// The container type of a [`GuiBlock`], which determines which properties are accepted.
1098/// Can be either a [`BuiltinWidget`] or a [`WidgetProperty`] of the [`GuiValidation::ComplexProperty`] type.
1099#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1100pub enum PropertyContainer {
1101    // The widget's ultimate base type.
1102    BuiltinWidget(BuiltinWidget),
1103    // A property that can hold other properties.
1104    ComplexProperty(WidgetProperty),
1105    // A property that can hold a widget.
1106    WidgetProperty(WidgetProperty),
1107}
1108
1109impl From<BuiltinWidget> for PropertyContainer {
1110    fn from(w: BuiltinWidget) -> Self {
1111        PropertyContainer::BuiltinWidget(w)
1112    }
1113}
1114
1115#[derive(Debug, Error, Copy, Clone, PartialEq, Eq, Hash)]
1116pub enum WidgetPropertyContainerError {
1117    #[error("property `{0}` cannot be a container")]
1118    WrongPropertyKind(WidgetProperty),
1119}
1120
1121impl TryFrom<WidgetProperty> for PropertyContainer {
1122    type Error = WidgetPropertyContainerError;
1123    fn try_from(prop: WidgetProperty) -> Result<Self, Self::Error> {
1124        let validation = GuiValidation::from_property(prop);
1125        if validation == GuiValidation::ComplexProperty {
1126            Ok(PropertyContainer::ComplexProperty(prop))
1127        } else if validation == GuiValidation::Widget {
1128            Ok(PropertyContainer::WidgetProperty(prop))
1129        } else {
1130            Err(WidgetPropertyContainerError::WrongPropertyKind(prop))
1131        }
1132    }
1133}
1134
1135impl Display for PropertyContainer {
1136    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
1137        match self {
1138            PropertyContainer::BuiltinWidget(builtin) => write!(f, "builtin widget {builtin}"),
1139            PropertyContainer::ComplexProperty(prop) => write!(f, "complex property {prop}"),
1140            PropertyContainer::WidgetProperty(prop) => write!(f, "widget property {prop}"),
1141        }
1142    }
1143}