operad 8.0.1

A cross-platform GUI library for Rust.
Documentation
use std::ops::Range;

use crate::actions::{action_target_enabled, keyboard_activation_key};
use crate::core::document::rect_is_finite;
use crate::platform::{
    ClipboardRequest, LogicalRect, PlatformRequest, TextImeRequest, TextImeResponse,
    TextImeSession, TextInputId, TextRange,
};
use taffy::prelude::{
    AlignItems, Dimension, Display, FlexDirection, JustifyContent, LengthPercentage,
    LengthPercentageAuto, Size as TaffySize, Style,
};

use super::*;

pub mod button;
pub mod canvas;
pub mod checkbox;
pub mod collapsing;
pub mod combo_box;
pub mod container;
pub mod drag_drop;
pub mod drag_value;
pub mod ext;
pub mod form;
pub mod grid;
pub mod helpers;
pub mod image;
pub mod label;
pub mod modal;
pub mod panel;
pub mod radio;
pub mod scroll_area;
pub mod scrollbar;
pub mod separator;
pub mod slider;
pub mod spinner;
pub mod table;
#[cfg(test)]
mod tests;
pub mod text_input;
pub mod theme_preference;
pub mod toggle;
pub mod tooltip;
pub mod virtual_list;

pub use button::{
    button, icon_button, image_button, reset_button, small_button, toggle_button, ButtonOptions,
};
pub use canvas::{canvas, CanvasOptions};
pub use checkbox::{checkbox, CheckboxOptions};
pub use collapsing::{collapsing_header, CollapsingHeaderNodes, CollapsingHeaderOptions};
pub use combo_box::{combo_box, ComboBoxOptions};
pub use container::{
    columns, frame, group, indented_section, resize_container, scene, sides, ColumnsNodes,
    ColumnsOptions, FrameOptions, IndentOptions, ResizeContainerNodes, ResizeContainerOptions,
    SceneOptions, SidesNodes, SidesOptions,
};
pub use drag_drop::{
    dnd_drag_source, dnd_drop_zone, DragImagePolicy, DragSourceNodes, DragSourceOptions,
    DropZoneNodes, DropZoneOptions,
};
pub use drag_value::{drag_angle, drag_angle_tau, drag_value_input, DragValueOptions};
pub use ext::dialog::{DialogDismissReason, DialogDismissal};
pub use ext::numeric_input::{NumericPrecision, NumericRange, NumericUnitFormat};
pub use ext::toggle_control::ToggleValue;
pub use form::{
    field_help_text, field_label, field_validation_message, form_action_buttons,
    form_error_summary, form_field_order, form_has_errors, form_row, form_section, next_form_field,
    previous_form_field, validation_text_style, FieldHelpOptions, FieldLabelOptions,
    FormActionAvailability, FormActionButtonNodes, FormActionButtonsOptions, FormActionKind,
    FormActionLabels, FormErrorSummaryNodes, FormErrorSummaryOptions, FormRowOptions,
    FormSectionNodes, FormSectionOptions, ValidationMessageOptions,
};
pub use helpers::{
    add_enabled, add_enabled_ui, add_visible, add_visible_ui, scroll_to_cursor, scroll_to_rect,
    scroll_to_rect_with_options, set_subtree_enabled, set_subtree_visible,
};
pub use image::{image, ImageOptions};
pub use label::{
    code_label, code_text_style, colored_label, colored_text_style, heading_label,
    heading_text_style, hyperlink, label, link, localized_label, monospace_label,
    monospace_text_style, selectable_label, selectable_value, small_label, small_text_style,
    strong_label, strong_text_style, weak_label, weak_text_style, wrapped_label, LinkOptions,
    SelectableLabelOptions,
};
pub use modal::{modal_dialog, ModalDialogNodes, ModalDialogOptions};
pub use panel::{
    bottom_panel, central_panel, group_panel, left_panel, panel, right_panel, side_panel,
    top_panel, PanelKind, PanelOptions, SidePanelSide,
};
pub use radio::{radio_button, radio_group, RadioButtonOptions, RadioGroupOptions, RadioOption};
pub use scroll_area::{
    scroll_area, scroll_container, ScrollContainerNodes, ScrollContainerOptions,
};
pub use separator::{separator, spacer, SeparatorOptions, SeparatorOrientation};
pub use slider::{slider, SliderClamping, SliderOptions, SliderThumbShape, SliderValueSpec};
pub use spinner::{spinner, SpinnerOptions};
pub use table::{table_header, TableColumn};
pub use text_input::{
    code_editor, multiline_text_input, password_input, search_input, selectable_text,
    singleline_text_input, text_area, text_input, TextInputInteractionPolicy, TextInputOptions,
    TextInputState,
};
pub use theme_preference::{
    theme_preference_buttons, theme_preference_switch, ThemePreference, ThemePreferenceButtonNodes,
    ThemePreferenceButtonsOptions, ThemePreferenceLabels, ThemePreferenceSwitchOptions,
};
pub use toggle::{toggle_switch, ToggleSwitchOptions};
pub use tooltip::{tooltip_box, TooltipBoxOptions, TooltipTriggerMode, TooltipTriggerOptions};
pub use virtual_list::{virtual_list, VirtualListSpec};

pub(crate) fn single_line_text_style(mut style: TextStyle) -> TextStyle {
    style.wrap = TextWrap::None;
    style
}

pub(crate) fn inline_intrinsic_chrome_size(style: &Style, inline_items: usize) -> UiSize {
    UiSize::new(
        horizontal_padding_width(style.padding)
            + horizontal_gap_width(style.gap.width, inline_items),
        vertical_padding_height(style.padding),
    )
}

pub(crate) fn inline_intrinsic_base_size(
    style: &Style,
    fixed_items: &[&Style],
    inline_items: usize,
) -> UiSize {
    let mut size = inline_intrinsic_chrome_size(style, inline_items);
    for fixed_item in fixed_items {
        let item_size = inline_item_outer_size(fixed_item);
        size.width += item_size.width;
        size.height = size.height.max(item_size.height);
    }
    if let Some(height) =
        dimension_points(style.min_size.height).or_else(|| dimension_points(style.size.height))
    {
        size.height = size.height.max(height);
    }
    size
}

pub(crate) fn publish_inline_intrinsic_size(
    document: &mut UiDocument,
    target: UiNodeId,
    sources: impl Into<Vec<UiNodeId>>,
    min_size: UiSize,
) {
    document.node_mut(target).layout_constraint =
        Some(UiNodeLayoutConstraint::InlineIntrinsicSize {
            sources: sources.into(),
            min_size,
        });
}

fn inline_item_outer_size(style: &Style) -> UiSize {
    UiSize::new(
        dimension_points(style.min_size.width)
            .or_else(|| dimension_points(style.size.width))
            .unwrap_or(0.0)
            + horizontal_margin_width(style.margin),
        dimension_points(style.min_size.height)
            .or_else(|| dimension_points(style.size.height))
            .unwrap_or(0.0)
            + vertical_margin_height(style.margin),
    )
}

fn horizontal_gap_width(gap: LengthPercentage, inline_items: usize) -> f32 {
    if inline_items <= 1 {
        0.0
    } else {
        length_percentage_points(gap) * (inline_items - 1) as f32
    }
}

fn horizontal_padding_width(
    padding: taffy::prelude::Rect<taffy::prelude::LengthPercentage>,
) -> f32 {
    length_percentage_points(padding.left) + length_percentage_points(padding.right)
}

fn vertical_padding_height(padding: taffy::prelude::Rect<taffy::prelude::LengthPercentage>) -> f32 {
    length_percentage_points(padding.top) + length_percentage_points(padding.bottom)
}

fn horizontal_margin_width(margin: taffy::prelude::Rect<LengthPercentageAuto>) -> f32 {
    length_percentage_auto_points(margin.left) + length_percentage_auto_points(margin.right)
}

fn vertical_margin_height(margin: taffy::prelude::Rect<LengthPercentageAuto>) -> f32 {
    length_percentage_auto_points(margin.top) + length_percentage_auto_points(margin.bottom)
}

fn dimension_points(value: Dimension) -> Option<f32> {
    let raw = value.into_raw();
    (raw.tag() == taffy::prelude::CompactLength::LENGTH_TAG).then_some(raw.value())
}

fn length_percentage_points(value: taffy::prelude::LengthPercentage) -> f32 {
    let raw = value.into_raw();
    if raw.tag() == taffy::prelude::CompactLength::LENGTH_TAG {
        raw.value().max(0.0)
    } else {
        0.0
    }
}

fn length_percentage_auto_points(value: LengthPercentageAuto) -> f32 {
    let raw = value.into_raw();
    if raw.tag() == taffy::prelude::CompactLength::LENGTH_TAG {
        raw.value().max(0.0)
    } else {
        0.0
    }
}