stereokit_rust/
ui.rs

1use crate::{
2    StereoKitError,
3    material::{Material, MaterialT},
4    maths::{Bool32T, Bounds, Pose, Vec2, Vec3},
5    mesh::{Mesh, MeshT, Vertex},
6    model::{Model, ModelT},
7    sound::{Sound, SoundT},
8    sprite::{Sprite, SpriteT},
9    system::{Align, BtnState, Handed, HierarchyParent, Log, TextContext, TextFit, TextStyle},
10    util::{Color32, Color128},
11};
12use std::{
13    ffi::{CStr, CString, c_char, c_ushort},
14    ptr::{NonNull, null_mut},
15};
16
17/// A description of what type of window to draw! This is a bit flag, so it can contain multiple elements.
18/// <https://stereokit.net/Pages/StereoKit/UIWin.html>
19///
20/// see [`Ui::window_begin`]
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22#[repr(u32)]
23pub enum UiWin {
24    /// No body, no head. Not really a flag, just set to this value. The Window will still be grab/movable. To prevent
25    /// it from being grabbable, combine with the UIMove.None option, or switch to Ui::(push/pop)_surface.
26    Empty = 1,
27    /// Flag to include a head on the window.
28    Head = 2,
29    /// Flag to include a body on the window.
30    Body = 4,
31    /// A normal window has a head and a body to it. Both can be grabbed.
32    Normal = 6,
33}
34
35/// This describes how a UI element moves when being dragged around by a user!
36/// <https://stereokit.net/Pages/StereoKit/UIMove.html>
37///
38/// see [`Ui::window_begin`] [`Ui::handle_begin`] [`Ui::handle`] [`Ui::system_move_type`]
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40#[repr(u32)]
41pub enum UiMove {
42    /// The element follows the position and orientation of the user’s hand exactly.
43    Exact = 0,
44    /// The element follows the position of the user’s hand, but orients to face the user’s head instead of just using
45    /// the hand’s rotation.
46    FaceUser = 1,
47    /// This element follows the hand’s position only, completely discarding any rotation information.
48    PosOnly = 2,
49    /// Do not allow user input to change the element’s pose at all! You may also be interested in Ui::(push/pop)_surface.
50    None = 3,
51}
52
53/// This describes how a layout should be cut up! Used with Ui::layout_push_cut.
54/// <https://stereokit.net/Pages/StereoKit/UICut.html>
55///
56/// see [`Ui::layout_push_cut`]
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58#[repr(u32)]
59pub enum UiCut {
60    /// This cuts a chunk from the left side of the current layout. This will work for layouts that are auto-sizing, and
61    /// fixed sized.
62    Left = 0,
63    /// This cuts a chunk from the right side of the current layout. This will work for layouts that are fixed sized,
64    /// but not layouts that auto-size on the X axis!
65    Right = 1,
66    /// This cuts a chunk from the top side of the current layout. This will work for layouts that are auto-sizing, and
67    /// fixed sized.
68    Top = 2,
69    /// This cuts a chunk from the bottom side of the current layout. This will work for layouts that are fixed sized,
70    /// but not layouts that auto-size on the Y axis!
71    Bottom = 3,
72}
73
74/// Theme color categories to pair with Ui::set_theme_color.
75/// The total lenght is `[u32,u32]` where the fist u32 is the enum and the second is the ExtraSlot value
76/// native C function should convert this to UiColorT
77/// <https://stereokit.net/Pages/StereoKit/UIColor.html>
78///
79/// see [`Ui::set_theme_color`] [`Ui::set_element_color`]
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81#[repr(u32)]
82pub enum UiColor {
83    /// he default category, used to indicate that no category has been selected.
84    None = 0,
85    /// This is the main accent color used by window headers, separators, etc.
86    Primary = 1,
87    /// This is a background sort of color that should generally be dark. Used by window bodies and backgrounds of
88    /// certain elements.
89    Background = 2,
90    /// A normal UI element color, for elements like buttons and sliders.
91    Common = 3,
92    /// Not really used anywhere at the moment, maybe for the Ui::panel.
93    Complement = 4,
94    /// Text color! This should generally be really bright, and at the very least contrast-ey.
95    Text = 5,
96    /// A maximum enum value to allow for iterating through enum values.
97    Max = 6,
98    /// All the extra color slots
99    ExtraSlot01,
100    ExtraSlot02,
101    ExtraSlot03,
102    ExtraSlot04,
103    ExtraSlot05,
104    ExtraSlot06,
105    ExtraSlot07,
106    ExtraSlot08,
107    ExtraSlot09,
108    ExtraSlot10,
109    ExtraSlot11,
110    ExtraSlot12,
111    ExtraSlot13,
112    ExtraSlot14,
113    ExtraSlot15,
114    ExtraSlot16,
115}
116
117/// Indicates the state of a UI theme color.
118/// <https://stereokit.net/Pages/StereoKit/UIColorState.html>
119///
120/// see [`Ui::set_theme_color`]
121#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122#[repr(u32)]
123pub enum UiColorState {
124    /// The UI element is in its normal resting state.
125    Normal = 0,
126    /// The UI element has been activated fully by some type of interaction.
127    Active = 1,
128    /// The UI element is currently disabled, and cannot be used.
129    Disabled = 2,
130}
131
132/// Used with StereoKit’s UI, and determines the interaction confirmation behavior for certain elements, such as the
133/// Ui::hslider!
134/// <https://stereokit.net/Pages/StereoKit/UIConfirm.html>
135///
136/// see [`Ui::hslider`]  [`Ui::vslider`]  [`Ui::slider_behavior`] [`Ui::volume_at`]
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138#[repr(u32)]
139pub enum UiConfirm {
140    /// The user must push a button with their finger to confirm interaction with this element. This is simpler to
141    /// activate as it requires no learned gestures, but may result in more false positives.
142    Push = 0,
143    /// The user must use a pinch gesture to interact with this element. This is much harder to activate by accident,
144    /// but does require the user to make a precise pinch gesture. You can pretty much be sure that’s what the user
145    /// meant to do!
146    Pinch = 1,
147    /// HSlider specific. Same as Pinch, but pulling out from the slider creates a scaled slider that lets you adjust
148    /// the slider at a more granular resolution.
149    VariablePinch = 2,
150}
151
152/// Describes the layout of a button with image/text contents! You can think of the naming here as being the location of
153/// the image, with the text filling the remaining space.
154/// <https://stereokit.net/Pages/StereoKit/UIBtnLayout.html>
155///
156/// see [`Ui::button_img`]  [`Ui::radio_img`]  [`Ui::toggle_img`]
157#[derive(Debug, Clone, Copy, PartialEq, Eq)]
158#[repr(u32)]
159pub enum UiBtnLayout {
160    /// Hide the image, and only show text.
161    None = 0,
162    /// Image to the left, text to the right. Image will take up no more than half the width.
163    Left = 1,
164    /// Image to the right, text to the left. Image will take up no more than half the width.
165    Right = 2,
166    /// Image will be centered in the button, and fill up the button as though it was the only element. Text will cram
167    /// itself under the padding below the image.
168    Center = 3,
169    /// Same as Center, but omitting the text.
170    CenterNoText = 4,
171}
172
173/// Determines when this UI function returns true.
174/// <https://stereokit.net/Pages/StereoKit/UINotify.html>
175///
176/// see [`Ui::vslider`]  [`Ui::hslider`]  
177#[derive(Debug, Clone, Copy, PartialEq, Eq)]
178#[repr(u32)]
179pub enum UiNotify {
180    /// This function returns true any time the values has changed!
181    Change = 0,
182    /// This function returns true when the user has finished interacting with it. This does not guarantee the value has
183    /// changed.
184    Finalize = 1,
185}
186
187/// This is a bit flag that describes different types and combinations of gestures used within the UI system.
188/// <https://stereokit.net/Pages/StereoKit/UIGesture.html>
189///
190/// see [`Ui::handle`]  [`Ui::handle_begin`]  
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192#[repr(u32)]
193pub enum UiGesture {
194    /// Default zero state, no gesture at all.
195    None = 0,
196    /// A pinching action, calculated by taking the distance between the tip of the thumb and the index finger.
197    Pinch = 1,
198    /// A gripping or grasping motion meant to represent a full hand grab. This is calculated using the distance between
199    /// the root and the tip of the ring finger.
200    Grip = 2,
201    /// This is a bit flag combination of both Pinch and Grip.
202    PinchGrip = 3,
203}
204
205/// <https://stereokit.net/Pages/StereoKit/UIPad.html>
206///
207/// see [`Ui::panel_begin`]  [`Ui::panel_at`]
208#[derive(Debug, Clone, Copy, PartialEq, Eq)]
209#[repr(u32)]
210pub enum UiPad {
211    None = 0,
212    Inside = 1,
213    Outside = 2,
214}
215
216/// Used with StereoKit’s UI to indicate a particular type of UI element visual.
217/// <https://stereokit.net/Pages/StereoKit/UIVisual.html>
218///
219/// see [`Ui::set_element_visual`]  [`Ui::set_element_color`] [`Ui::draw_element`] [`Ui::set_element_sound`]
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
221#[repr(u32)]
222pub enum UiVisual {
223    /// Default state, no UI element at all.
224    None = 0,
225    /// A default root UI element. Not a particular element, but other elements may refer to this if there is nothing
226    /// more specific present.
227    Default = 1,
228    /// Refers to Ui::button elements.
229    Button = 2,
230    /// Refers to Ui::toggle elements.
231    Toggle = 3,
232    /// Refers to Ui::input elements.
233    Input = 4,
234    /// Refers to Ui::handle/handle_begin elements.
235    Handle = 5,
236    /// Refers to UI::window/window_begin body panel element, this element is used when a Window head is also present.
237    WindowBody = 6,
238    /// Refers to Ui::window/window_begin body element, this element is used when a Window only has the body panel,
239    /// without a head.
240    WindowBodyOnly = 7,
241    /// Refers to Ui::window/window_begin head panel element, this element is used when a Window body is also present.
242    WindowHead = 8,
243    /// Refers to Ui::window/window_begin head element, this element is used when a Window only has the head panel,
244    /// without a body.
245    WindowHeadOnly = 9,
246    /// Refers to Ui::hseparator element.
247    Separator = 10,
248    /// Refers to the back line component of the Ui::hslider element for full lines.
249    SliderLine = 11,
250    /// Refers to the back line component of the Ui::hslider element for the active or “full” half of the line.
251    SliderLineActive = 12,
252    /// Refers to the back line component of the Ui::hslider element for the inactive or “empty” half of the line.
253    SliderLineInactive = 13,
254    /// Refers to the push button component of the Ui::hslider element when using UiConfirm::Push.
255    SliderPush = 14,
256    /// Refers to the pinch button component of the Ui::hslider element when using UiConfirm::Pinch.
257    SliderPinch = 15,
258    /// Refers to Ui::button_round elements.
259    ButtonRound = 16,
260    /// Refers to Ui::panel_(begin/end) elements.
261    Panel = 17,
262    /// Refers to the text position indicator carat on text input elements.
263    Carat = 18,
264    /// An aura ...
265    Aura = 19,
266    /// A maximum enum value to allow for iterating through enum values.
267    Max = 20,
268    /// All the extra color slots
269    ExtraSlot01,
270    ExtraSlot02,
271    ExtraSlot03,
272    ExtraSlot04,
273    ExtraSlot05,
274    ExtraSlot06,
275    ExtraSlot07,
276    ExtraSlot08,
277    ExtraSlot09,
278    ExtraSlot10,
279    ExtraSlot11,
280    ExtraSlot12,
281    ExtraSlot13,
282    ExtraSlot14,
283    ExtraSlot15,
284    ExtraSlot16,
285}
286
287/// For UI elements that can be oriented horizontally or vertically, this specifies that orientation.
288/// <https://stereokit.net/Pages/StereoKit/UIDir.html>
289///
290/// see [`Ui::progress_bar_at`]
291#[derive(Debug, Clone, Copy, PartialEq, Eq)]
292#[repr(u32)]
293pub enum UiDir {
294    /// The element should be layed out along the horizontal axis.
295    Horizontal,
296    /// The element should be layed out along the vertical axis.
297    Vertical,
298}
299
300bitflags::bitflags! {
301/// For elements that contain corners, this bit flag allows you to specify which corners.
302/// <https://stereokit.net/Pages/StereoKit/UICorner.html>
303///
304/// see [`Ui::gen_quadrant_mesh`]
305#[derive(Debug, Clone, Copy, PartialEq, Eq)]
306#[repr(C)]
307pub struct UiCorner : u32
308{
309    /// No corners at all.
310    const None        = 0;
311    /// The top right corner.
312    const TopRight    = 1 << 1;
313    /// The top left corner.
314    const TopLeft     = 1 << 0;
315    /// The bottom left corner.
316    const BottomLeft  = 1 << 3;
317    /// The bottom right corner.
318    const BottomRight = 1 << 2;
319    /// All corners.
320    const All    = Self::TopLeft.bits()    | Self::TopRight.bits() | Self::BottomLeft.bits() | Self::BottomRight.bits();
321    /// The top left and top right corners.
322    const Top    = Self::TopLeft.bits()    | Self::TopRight.bits();
323    /// The bottom left and bottom right corners.
324    const Bottom = Self::BottomLeft.bits() | Self::BottomRight.bits();
325    /// The top left and bottom left corners.
326    const Left   = Self::TopLeft.bits()    | Self::BottomLeft.bits();
327    /// The top right and bottom right corners.
328    const Right  = Self::TopRight.bits()   | Self::BottomRight.bits();
329}
330}
331
332bitflags::bitflags! {
333/// This describes how UI elements with scrollable regions scroll
334/// around or use scroll bars! This allows you to enable or disable
335/// vertical and horizontal scrolling.
336/// <https://stereokit.net/Pages/StereoKit/UIScroll.html>
337///
338/// see [`Ui::text`] [`Ui::text_at`]
339#[derive(Debug, Clone, Copy, PartialEq, Eq)]
340#[repr(C)]
341pub struct UiScroll : u32
342{
343    /// No scroll bars or scrolling.
344    const None       = 0;
345    /// This will enable vertical scroll bars or scrolling.
346    const Vertical   = 1 << 0;
347    /// This will enable horizontal scroll bars or scrolling.
348    const Horizontal = 1 << 1;
349    /// This will enable both vertical and horizontal scroll bars
350    /// or scrolling.
351    const Both = Self::Vertical.bits() | Self::Horizontal.bits();
352}
353}
354
355/// A point on a lathe for a mesh generation algorithm. This is the 'silhouette' of the mesh, or the shape the mesh
356/// would take if you spun this line of points in a cylinder.
357/// <https://stereokit.net/Pages/StereoKit/UILathePt.html>
358///
359/// see [`Ui::gen_quadrant_mesh`]
360#[derive(Debug, Copy, Clone)]
361#[repr(C)]
362pub struct UiLathePt {
363    /// Lathe point 'location', where 'x' is a percentage of the lathe radius alnong the current surface normal, and Y
364    /// is the absolute Z axis value.
365    pub pt: Vec2,
366    /// The lathe normal point, which will be rotated along the surface of the mesh.
367    pub normal: Vec2,
368    /// Vertex color of the current lathe vertex.
369    pub color: Color32,
370    /// Will there be triangles connecting this lathe point to the next in the list, or is this a jump without
371    /// triangles?
372    pub connect_next: Bool32T,
373    /// Should the triangles attaching this point to the next be ordered backwards?
374    pub flip_face: Bool32T,
375}
376
377impl UiLathePt {
378    pub fn new(
379        pt: impl Into<Vec2>,
380        normal: impl Into<Vec2>,
381        color: Color32,
382        connect_next: bool,
383        flip_face: bool,
384    ) -> Self {
385        Self {
386            pt: pt.into(),
387            normal: normal.into(),
388            color,
389            connect_next: connect_next.into(),
390            flip_face: flip_face.into(),
391        }
392    }
393    /// This is a default lathe for a button!
394    /// <https://stereokit.net/Pages/StereoKit/UI/GenQuadrantMesh.html>
395    ///
396    /// see also [`ui_gen_quadrant_mesh`]
397    pub fn button() -> [UiLathePt; 6] {
398        let white = Color32::WHITE;
399        let black = Color32::BLACK;
400        let shadow_center = Color32::rgba(0, 0, 0, 200);
401        [
402            UiLathePt::new([0.00, -0.5], [0.0, 1.0], white, true, false),
403            UiLathePt::new([0.95, -0.5], [0.0, 1.0], white, true, false),
404            UiLathePt::new([1.0, -0.45], [1.0, 0.0], white, true, false),
405            UiLathePt::new([1.00, -0.1], [1.0, 0.0], white, false, false),
406            UiLathePt::new([1.20, 0.49], [0.0, 1.0], black, true, true),
407            UiLathePt::new([0.00, 0.49], [0.0, 1.0], shadow_center, true, true),
408        ]
409    }
410    /// This is a default lathe for an input!
411    /// <https://stereokit.net/Pages/StereoKit/UI/GenQuadrantMesh.html>
412    ///
413    /// see also [`ui_gen_quadrant_mesh`]
414    pub fn input() -> [UiLathePt; 10] {
415        let gray = Color32::rgba(200, 200, 200, 255);
416        let white = Color32::WHITE;
417        let black = Color32::BLACK;
418        let shadow_center = Color32::rgba(0, 0, 0, 200);
419        [
420            UiLathePt::new([0.00, -0.1], [0.0, 1.0], gray, true, false),
421            UiLathePt::new([0.80, -0.1], [0.0, 1.0], gray, false, false),
422            UiLathePt::new([0.80, -0.1], [-1.0, 0.0], gray, true, false),
423            UiLathePt::new([0.80, -0.5], [-1.0, 0.0], white, false, false),
424            UiLathePt::new([0.80, -0.5], [0.0, 1.0], white, true, false),
425            UiLathePt::new([0.95, -0.5], [0.0, 1.0], white, true, false),
426            UiLathePt::new([1.0, -0.45], [1.0, 0.0], white, true, false),
427            UiLathePt::new([1.00, -0.1], [1.0, 0.0], white, false, false),
428            UiLathePt::new([1.20, 0.49], [0.0, 1.0], black, true, true),
429            UiLathePt::new([0.00, 0.49], [0.0, 1.0], shadow_center, true, true),
430        ]
431    }
432
433    /// This is a default lathe for a plane!
434    /// <https://stereokit.net/Pages/StereoKit/UI/GenQuadrantMesh.html>
435    ///
436    /// see also [`ui_gen_quadrant_mesh`]
437    pub fn plane() -> [UiLathePt; 2] {
438        let white = Color32::WHITE;
439        [
440            UiLathePt::new([0.0, 0.0], [0.0, 1.0], white, true, false),
441            UiLathePt::new([1.0, 0.0], [0.0, 1.0], white, false, false),
442        ]
443    }
444
445    /// This is a default lathe for a panel!
446    /// <https://stereokit.net/Pages/StereoKit/UI/GenQuadrantMesh.html>
447    ///
448    /// see also [`ui_gen_quadrant_mesh`]
449    pub fn panel() -> [UiLathePt; 6] {
450        let white = Color32::WHITE;
451        [
452            UiLathePt::new([0.0, -0.5], [0.0, 1.0], white, true, false),
453            UiLathePt::new([1.0, -0.5], [0.0, 1.0], white, false, false),
454            UiLathePt::new([1.0, -0.5], [1.0, 0.0], white, true, false),
455            UiLathePt::new([1.0, 0.5], [1.0, 0.0], white, false, false),
456            UiLathePt::new([1.0, 0.5], [0.0, -1.0], white, true, false),
457            UiLathePt::new([0.0, 0.5], [0.0, -1.0], white, true, false),
458        ]
459    }
460    /// This is a default lathe for a slider!
461    /// <https://stereokit.net/Pages/StereoKit/UI/GenQuadrantMesh.html>
462    ///
463    /// see also [`ui_gen_quadrant_mesh`]
464    pub fn slider() -> [UiLathePt; 6] {
465        let white = Color32::WHITE;
466        let black = Color32::BLACK;
467        let shadow_edge = Color32::rgba(0, 0, 0, 100);
468        [
469            UiLathePt::new([0.0, -0.5], [0.0, 1.0], white, true, false),
470            UiLathePt::new([1.0, -0.5], [0.0, 1.0], white, false, false),
471            UiLathePt::new([1.0, -0.5], [1.0, 0.0], white, true, false),
472            UiLathePt::new([1.0, 0.5], [1.0, 0.0], white, false, false),
473            UiLathePt::new([1.0, 0.49], [0.0, 1.0], shadow_edge, true, false),
474            UiLathePt::new([2.0, 0.49], [0.0, 1.0], black, false, false),
475        ]
476    }
477
478    /// This is a default lathe for a slider button!
479    /// <https://stereokit.net/Pages/StereoKit/UI/GenQuadrantMesh.html>
480    ///
481    /// see also [`ui_gen_quadrant_mesh`]
482    pub fn slider_btn() -> [UiLathePt; 6] {
483        let white = Color32::WHITE;
484        let black = Color32::BLACK;
485        let shadow_edge = Color32::rgba(0, 0, 0, 100);
486        [
487            UiLathePt::new([0.0, -0.5], [0.0, 1.0], white, true, false),
488            UiLathePt::new([0.8, -0.5], [0.0, 1.0], white, true, false),
489            UiLathePt::new([1.0, -0.4], [1.0, 0.0], white, true, false),
490            UiLathePt::new([1.0, 0.5], [1.0, 0.0], white, false, false),
491            UiLathePt::new([1.0, 0.49], [0.0, 1.0], shadow_edge, true, false),
492            UiLathePt::new([2.0, 0.49], [0.0, 1.0], black, false, false),
493        ]
494    }
495}
496
497#[derive(Debug, Copy, Clone)]
498#[repr(C)]
499/// Visual properties and spacing of the UI system.
500/// <https://stereokit.net/Pages/StereoKit/UISettings.html>
501///
502/// see [`Ui::settings`]
503pub struct UiSettings {
504    /// The margin is the space between a window and its contents. In meters.
505    pub margin: f32,
506    /// Spacing between an item and its parent, in meters.
507    pub padding: f32,
508    /// Spacing between sibling items, in meters.
509    pub gutter: f32,
510    /// The Z depth of 3D UI elements, in meters.
511    pub depth: f32,
512    /// Radius of the UI element corners, in meters.
513    pub rounding: f32,
514    /// How far up does the white back-border go on UI elements? This is a 0-1 percentage of the depth value.
515    pub backplate_depth: f32,
516    // How wide is the back-border around the UI elements? In meters.
517    pub backplate_border: f32,
518    /// Defines the scale factor for the separator's thickness. The thickness is calculated by multiplying the height
519    /// of the text by this factor. The default valus is 0.4f.
520    pub separator_scale: f32,
521}
522
523/// StereoKit ffi type.
524/// see also [`crate::util::Hash`] [`Ui::stack_hash`] [`Ui::push_id`] [`Ui::push_id_int`]
525pub type IdHashT = u64;
526
527#[derive(Default, Debug, Copy, Clone)]
528#[repr(C)]
529/// Visual properties of a slider behavior.
530/// <https://stereokit.net/Pages/StereoKit/UISliderData.html>
531///
532/// see [`Ui::slider_behavior`]
533pub struct UiSliderData {
534    /// The center location of where the slider's interactionelement is.
535    pub button_center: Vec2,
536    /// The current distance of the finger, within the pressable volume of the slider, from the bottom of the slider
537    pub finger_offset: f32,
538    /// This is the current frame's "focus" state for the button.
539    pub focus_state: BtnState,
540    /// This is the current frame's "active" state for the button.
541    pub active_state: BtnState,
542    /// The interactor that is currently driving the activity or focus of the slider. Or -1 if there is no interaction.
543    pub interactor: i32,
544}
545
546/// This class is a collection of user interface and interaction methods! StereoKit uses an Immediate Mode GUI system,
547/// which can be very easy to work with and modify during runtime.
548///
549/// You must call the UI method every frame you wish it to be available, and if you no longer want it to be present, you
550/// simply stop calling it! The id of the element is used to track its state from frame to frame, so for elements with
551/// state, you’ll want to avoid changing the id during runtime! Ids are also scoped per-window, so different windows can
552/// re-use the same id, but a window cannot use the same id twice.
553/// <https://stereokit.net/Pages/StereoKit/UI.html>
554/// ### Examples
555/// ```
556/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
557/// use stereokit_rust::{ui::{Ui,UiBtnLayout}, maths::{Vec2, Vec3, Pose}, sprite::Sprite};
558///
559/// let mut window_pose = Pose::new(
560///     [0.01, 0.09, 0.88], Some([0.0, 185.0, 0.0].into()));
561///
562/// let (mut choice, mut doubt, mut scaling) = ("C", false, 0.5);
563/// let (on, off) = (Sprite::radio_on(), Sprite::radio_off());
564/// let exit_sprite = Sprite::from_file("textures/exit.jpeg", None, None)
565///                               .expect("exit.jpeg should be ok");
566///
567/// filename_scr = "screenshots/ui.jpeg";
568/// test_screenshot!( // !!!! Get a proper main loop !!!!
569///     Ui::window_begin("Question", &mut window_pose, None, None, None);
570///     Ui::text("Are you a robot ?", None, None, None, Some(0.13), None, None);
571///     Ui::hseparator();
572///     Ui::label("Respond wisely", Some([0.08, 0.03].into()), false);
573///     if Ui::radio_img("yes", choice == "A", &off, &on, UiBtnLayout::Left, None) {
574///         choice = "A"; scaling = 0.0;
575///     }
576///     Ui::same_line();
577///     if Ui::radio_img("no", choice == "B", &off, &on, UiBtnLayout::Left, None){
578///         choice = "B"; scaling = 1.0;
579///     }
580///     Ui::same_line();
581///     if Ui::radio_img("maybe", choice == "C", &off, &on, UiBtnLayout::Left, None) {
582///         choice = "C"; scaling = 0.5;
583///     }
584///     Ui::panel_begin(None);
585///     Ui::toggle("Doubt value:", &mut doubt, None);
586///     Ui::push_enabled(doubt, None);
587///     Ui::hslider("scaling", &mut scaling, 0.0, 1.0,Some(0.05), Some(0.14), None, None);
588///     Ui::pop_enabled();
589///     Ui::panel_end();
590///     Ui::same_line();
591///     if Ui::button_img("Exit", &exit_sprite, Some(UiBtnLayout::CenterNoText),
592///                       Some(Vec2::new(0.08, 0.08)), None) {
593///        sk.quit(None);
594///     }
595///     Ui::window_end();
596/// );
597/// ```
598/// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui.jpeg" alt="screenshot" width="200">
599pub struct Ui;
600
601unsafe extern "C" {
602    pub fn ui_quadrant_size_verts(ref_vertices: *mut Vertex, vertex_count: i32, overflow_percent: f32);
603    pub fn ui_quadrant_size_mesh(ref_mesh: MeshT, overflow_percent: f32);
604    pub fn ui_gen_quadrant_mesh(
605        rounded_corners: UiCorner,
606        corner_radius: f32,
607        corner_resolution: u32,
608        delete_flat_sides: Bool32T,
609        quadrantify: Bool32T,
610        lathe_pts: *const UiLathePt,
611        lathe_pt_count: i32,
612    ) -> MeshT;
613    pub fn ui_show_volumes(show: Bool32T);
614    pub fn ui_enable_far_interact(enable: Bool32T);
615    pub fn ui_far_interact_enabled() -> Bool32T;
616    pub fn ui_system_get_move_type() -> UiMove;
617    pub fn ui_system_set_move_type(move_type: UiMove);
618    pub fn ui_settings(settings: UiSettings);
619    pub fn ui_get_settings() -> UiSettings;
620    pub fn ui_get_margin() -> f32;
621    pub fn ui_get_padding() -> f32;
622    pub fn ui_get_gutter() -> f32;
623    pub fn ui_set_color(color: Color128);
624    pub fn ui_set_theme_color(color_type: UiColor, color_gamma: Color128);
625    pub fn ui_get_theme_color(color_type: UiColor) -> Color128;
626    pub fn ui_set_theme_color_state(color_type: UiColor, state: UiColorState, color_gamma: Color128);
627    pub fn ui_get_theme_color_state(color_type: UiColor, state: UiColorState) -> Color128;
628    pub fn ui_set_element_visual(element_visual: UiVisual, mesh: MeshT, material: MaterialT, min_size: Vec2);
629    pub fn ui_set_element_color(element_visual: UiVisual, color_category: UiColor);
630    pub fn ui_set_element_sound(element_visual: UiVisual, activate: SoundT, deactivate: SoundT);
631    pub fn ui_has_keyboard_focus() -> Bool32T;
632    pub fn ui_popup_pose(shift: Vec3) -> Pose;
633
634    pub fn ui_draw_element(element_visual: UiVisual, start: Vec3, size: Vec3, focus: f32);
635    pub fn ui_draw_element_color(
636        element_visual: UiVisual,
637        element_color: UiVisual,
638        start: Vec3,
639        size: Vec3,
640        focus: f32,
641    );
642    pub fn ui_get_element_color(element_visual: UiVisual, focus: f32) -> Color128;
643    pub fn ui_get_anim_focus(id: IdHashT, focus_state: BtnState, activation_state: BtnState) -> f32;
644
645    pub fn ui_play_sound_on_off(element_visual: UiVisual, element_id: IdHashT, at_local: Vec3);
646    pub fn ui_play_sound_on(element_visual: UiVisual, at_local: Vec3);
647    pub fn ui_play_sound_off(element_visual: UiVisual, at_local: Vec3);
648
649    pub fn ui_push_grab_aura(enabled: Bool32T);
650    pub fn ui_pop_grab_aura();
651    pub fn ui_grab_aura_enabled() -> Bool32T;
652    pub fn ui_push_text_style(style: TextStyle);
653    pub fn ui_pop_text_style();
654    pub fn ui_get_text_style() -> TextStyle;
655    pub fn ui_is_enabled() -> Bool32T;
656    pub fn ui_push_tint(tint_gamma: Color128);
657    pub fn ui_pop_tint();
658    pub fn ui_push_enabled(enabled: Bool32T, parent_behavior: HierarchyParent);
659    pub fn ui_pop_enabled();
660    pub fn ui_push_preserve_keyboard(preserve_keyboard: Bool32T);
661    pub fn ui_pop_preserve_keyboard();
662    pub fn ui_push_surface(surface_pose: Pose, layout_start: Vec3, layout_dimensions: Vec2);
663    pub fn ui_pop_surface();
664    pub fn ui_push_id(id: *const c_char) -> IdHashT;
665    pub fn ui_push_id_16(id: *const c_ushort) -> IdHashT;
666    pub fn ui_push_idi(id: i32) -> IdHashT;
667    pub fn ui_pop_id();
668    pub fn ui_stack_hash(string: *const c_char) -> IdHashT;
669    pub fn ui_stack_hash_16(string: *const c_ushort) -> IdHashT;
670    pub fn ui_layout_area(start: Vec3, dimensions: Vec2, add_margin: Bool32T);
671    pub fn ui_layout_remaining() -> Vec2;
672    pub fn ui_layout_at() -> Vec3;
673    pub fn ui_layout_last() -> Bounds;
674    pub fn ui_layout_reserve(size: Vec2, add_padding: Bool32T, depth: f32) -> Bounds;
675    pub fn ui_layout_push(start: Vec3, dimensions: Vec2, add_margin: Bool32T);
676    pub fn ui_layout_push_cut(cut_to: UiCut, size: f32, add_margin: Bool32T);
677    pub fn ui_layout_pop();
678    // Deprecaded: pub fn ui_last_element_hand_used(hand: Handed) -> BtnState;
679    /// TODO: v0.4 These functions use hands instead of interactors, they need replaced!
680    pub fn ui_is_interacting(hand: Handed) -> Bool32T;
681    /// TODO: v0.4 These functions use hands instead of interactors, they need replaced!
682    pub fn ui_last_element_hand_active(hand: Handed) -> BtnState;
683    /// TODO: v0.4 These functions use hands instead of interactors, they need replaced!
684    pub fn ui_last_element_hand_focused(hand: Handed) -> BtnState;
685    pub fn ui_last_element_active() -> BtnState;
686    pub fn ui_last_element_focused() -> BtnState;
687    // Deprecated: pub fn ui_area_remaining() -> Vec2;
688    pub fn ui_nextline();
689    pub fn ui_sameline();
690    pub fn ui_line_height() -> f32;
691    pub fn ui_button_behavior(
692        window_relative_pos: Vec3,
693        size: Vec2,
694        id: IdHashT,
695        out_finger_offset: *mut f32,
696        out_button_state: *mut BtnState,
697        out_focus_state: *mut BtnState,
698        out_opt_hand: *mut i32,
699    );
700    pub fn ui_button_behavior_depth(
701        window_relative_pos: Vec3,
702        size: Vec2,
703        id: IdHashT,
704        button_depth: f32,
705        button_activation_depth: f32,
706        out_finger_offset: *mut f32,
707        out_button_state: *mut BtnState,
708        out_focus_state: *mut BtnState,
709        out_opt_hand: *mut i32,
710    );
711    pub fn ui_slider_behavior(
712        window_relative_pos: Vec3,
713        size: Vec2,
714        id: IdHashT,
715        value: *mut Vec2,
716        min: Vec2,
717        max: Vec2,
718        button_size_visual: Vec2,
719        button_size_interact: Vec2,
720        confirm_method: UiConfirm,
721        data: *mut UiSliderData,
722    );
723    pub fn ui_volume_at(
724        id: *const c_char,
725        bounds: Bounds,
726        interact_type: UiConfirm,
727        out_opt_hand: *mut Handed,
728        out_opt_focus_state: *mut BtnState,
729    ) -> BtnState;
730    pub fn ui_volume_at_16(
731        id: *const c_ushort,
732        bounds: Bounds,
733        interact_type: UiConfirm,
734        out_opt_hand: *mut Handed,
735        out_opt_focus_state: *mut BtnState,
736    ) -> BtnState;
737    // Deprecated : pub fn ui_volume_at(id: *const c_char, bounds: Bounds) -> Bool32T;
738    // Deprecated : pub fn ui_volume_at_16(id: *const c_ushort, bounds: Bounds) -> Bool32T;
739    // Deprecated : pub fn ui_interact_volume_at(bounds: Bounds, out_hand: *mut Handed) -> BtnState;
740    pub fn ui_label(text: *const c_char, use_padding: Bool32T);
741    pub fn ui_label_16(text: *const c_ushort, use_padding: Bool32T);
742    pub fn ui_label_sz(text: *const c_char, size: Vec2, use_padding: Bool32T);
743    pub fn ui_label_sz_16(text: *const c_ushort, size: Vec2, use_padding: Bool32T);
744    pub fn ui_text(
745        text: *const c_char,
746        scroll: *mut Vec2,
747        scroll_direction: UiScroll,
748        height: f32,
749        text_align: Align,
750    ) -> Bool32T;
751    pub fn ui_text_16(
752        text: *const c_ushort,
753        scroll: *mut Vec2,
754        scroll_direction: UiScroll,
755        height: f32,
756        text_align: Align,
757    ) -> Bool32T;
758    pub fn ui_text_sz(
759        text: *const c_char,
760        scroll: *mut Vec2,
761        scroll_direction: UiScroll,
762        size: Vec2,
763        text_align: Align,
764        fit: TextFit,
765    ) -> Bool32T;
766    pub fn ui_text_sz_16(
767        text: *const c_ushort,
768        scroll: *mut Vec2,
769        scroll_direction: UiScroll,
770        size: Vec2,
771        text_align: Align,
772        fit: TextFit,
773    ) -> Bool32T;
774    pub fn ui_text_at(
775        text: *const c_char,
776        scroll: *mut Vec2,
777        scroll_direction: UiScroll,
778        text_align: Align,
779        fit: TextFit,
780        window_relative_pos: Vec3,
781        size: Vec2,
782    ) -> Bool32T;
783    pub fn ui_text_at_16(
784        text: *const c_ushort,
785        scroll: *mut Vec2,
786        scroll_direction: UiScroll,
787        text_align: Align,
788        fit: TextFit,
789        window_relative_pos: Vec3,
790        size: Vec2,
791    ) -> Bool32T;
792    pub fn ui_button(text: *const c_char) -> Bool32T;
793    pub fn ui_button_16(text: *const c_ushort) -> Bool32T;
794    pub fn ui_button_sz(text: *const c_char, size: Vec2) -> Bool32T;
795    pub fn ui_button_sz_16(text: *const c_ushort, size: Vec2) -> Bool32T;
796    pub fn ui_button_at(text: *const c_char, window_relative_pos: Vec3, size: Vec2) -> Bool32T;
797    pub fn ui_button_at_16(text: *const c_ushort, window_relative_pos: Vec3, size: Vec2) -> Bool32T;
798    pub fn ui_button_img(text: *const c_char, image: SpriteT, image_layout: UiBtnLayout, color: Color128) -> Bool32T;
799    pub fn ui_button_img_16(
800        text: *const c_ushort,
801        image: SpriteT,
802        image_layout: UiBtnLayout,
803        color: Color128,
804    ) -> Bool32T;
805    pub fn ui_button_img_sz(
806        text: *const c_char,
807        image: SpriteT,
808        image_layout: UiBtnLayout,
809        size: Vec2,
810        color: Color128,
811    ) -> Bool32T;
812    pub fn ui_button_img_sz_16(
813        text: *const c_ushort,
814        image: SpriteT,
815        image_layout: UiBtnLayout,
816        size: Vec2,
817        color: Color128,
818    ) -> Bool32T;
819    pub fn ui_button_img_at(
820        text: *const c_char,
821        image: SpriteT,
822        image_layout: UiBtnLayout,
823        window_relative_pos: Vec3,
824        size: Vec2,
825        color: Color128,
826    ) -> Bool32T;
827    pub fn ui_button_img_at_16(
828        text: *const c_ushort,
829        image: SpriteT,
830        image_layout: UiBtnLayout,
831        window_relative_pos: Vec3,
832        size: Vec2,
833        color: Color128,
834    ) -> Bool32T;
835    pub fn ui_button_round(id: *const c_char, image: SpriteT, diameter: f32) -> Bool32T;
836    pub fn ui_button_round_16(id: *const c_ushort, image: SpriteT, diameter: f32) -> Bool32T;
837    pub fn ui_button_round_at(id: *const c_char, image: SpriteT, window_relative_pos: Vec3, diameter: f32) -> Bool32T;
838    pub fn ui_button_round_at_16(
839        id: *const c_ushort,
840        image: SpriteT,
841        window_relative_pos: Vec3,
842        diameter: f32,
843    ) -> Bool32T;
844    pub fn ui_toggle(text: *const c_char, pressed: *mut Bool32T) -> Bool32T;
845    pub fn ui_toggle_16(text: *const c_ushort, pressed: *mut Bool32T) -> Bool32T;
846    pub fn ui_toggle_sz(text: *const c_char, pressed: *mut Bool32T, size: Vec2) -> Bool32T;
847    pub fn ui_toggle_sz_16(text: *const c_ushort, pressed: *mut Bool32T, size: Vec2) -> Bool32T;
848    pub fn ui_toggle_at(text: *const c_char, pressed: *mut Bool32T, window_relative_pos: Vec3, size: Vec2) -> Bool32T;
849    pub fn ui_toggle_at_16(
850        text: *const c_ushort,
851        pressed: *mut Bool32T,
852        window_relative_pos: Vec3,
853        size: Vec2,
854    ) -> Bool32T;
855    pub fn ui_toggle_img(
856        text: *const c_char,
857        pressed: *mut Bool32T,
858        toggle_off: SpriteT,
859        toggle_on: SpriteT,
860        image_layout: UiBtnLayout,
861    ) -> Bool32T;
862    pub fn ui_toggle_img_16(
863        text: *const c_ushort,
864        pressed: *mut Bool32T,
865        toggle_off: SpriteT,
866        toggle_on: SpriteT,
867        image_layout: UiBtnLayout,
868    ) -> Bool32T;
869    pub fn ui_toggle_img_sz(
870        text: *const c_char,
871        pressed: *mut Bool32T,
872        toggle_off: SpriteT,
873        toggle_on: SpriteT,
874        image_layout: UiBtnLayout,
875        size: Vec2,
876    ) -> Bool32T;
877    pub fn ui_toggle_img_sz_16(
878        text: *const c_ushort,
879        pressed: *mut Bool32T,
880        toggle_off: SpriteT,
881        toggle_on: SpriteT,
882        image_layout: UiBtnLayout,
883        size: Vec2,
884    ) -> Bool32T;
885    pub fn ui_toggle_img_at(
886        text: *const c_char,
887        pressed: *mut Bool32T,
888        toggle_off: SpriteT,
889        toggle_on: SpriteT,
890        image_layout: UiBtnLayout,
891        window_relative_pos: Vec3,
892        size: Vec2,
893    ) -> Bool32T;
894    pub fn ui_toggle_img_at_16(
895        text: *const c_ushort,
896        pressed: *mut Bool32T,
897        toggle_off: SpriteT,
898        toggle_on: SpriteT,
899        image_layout: UiBtnLayout,
900        window_relative_pos: Vec3,
901        size: Vec2,
902    ) -> Bool32T;
903    pub fn ui_hslider(
904        id: *const c_char,
905        value: *mut f32,
906        min: f32,
907        max: f32,
908        step: f32,
909        width: f32,
910        confirm_method: UiConfirm,
911        notify_on: UiNotify,
912    ) -> Bool32T;
913    pub fn ui_hslider_16(
914        id: *const c_ushort,
915        value: *mut f32,
916        min: f32,
917        max: f32,
918        step: f32,
919        width: f32,
920        confirm_method: UiConfirm,
921        notify_on: UiNotify,
922    ) -> Bool32T;
923    pub fn ui_hslider_f64(
924        id: *const c_char,
925        value: *mut f64,
926        min: f64,
927        max: f64,
928        step: f64,
929        width: f32,
930        confirm_method: UiConfirm,
931        notify_on: UiNotify,
932    ) -> Bool32T;
933    pub fn ui_hslider_f64_16(
934        id: *const c_ushort,
935        value: *mut f64,
936        min: f64,
937        max: f64,
938        step: f64,
939        width: f32,
940        confirm_method: UiConfirm,
941        notify_on: UiNotify,
942    ) -> Bool32T;
943    pub fn ui_hslider_at(
944        id: *const c_char,
945        value: *mut f32,
946        min: f32,
947        max: f32,
948        step: f32,
949        window_relative_pos: Vec3,
950        size: Vec2,
951        confirm_method: UiConfirm,
952        notify_on: UiNotify,
953    ) -> Bool32T;
954    pub fn ui_hslider_at_16(
955        id: *const c_ushort,
956        value: *mut f32,
957        min: f32,
958        max: f32,
959        step: f32,
960        window_relative_pos: Vec3,
961        size: Vec2,
962        confirm_method: UiConfirm,
963        notify_on: UiNotify,
964    ) -> Bool32T;
965    pub fn ui_hslider_at_f64(
966        id: *const c_char,
967        value: *mut f64,
968        min: f64,
969        max: f64,
970        step: f64,
971        window_relative_pos: Vec3,
972        size: Vec2,
973        confirm_method: UiConfirm,
974        notify_on: UiNotify,
975    ) -> Bool32T;
976    pub fn ui_hslider_at_f64_16(
977        id: *const c_ushort,
978        value: *mut f64,
979        min: f64,
980        max: f64,
981        step: f64,
982        window_relative_pos: Vec3,
983        size: Vec2,
984        confirm_method: UiConfirm,
985        notify_on: UiNotify,
986    ) -> Bool32T;
987    pub fn ui_vslider(
988        id: *const c_char,
989        value: *mut f32,
990        min: f32,
991        max: f32,
992        step: f32,
993        height: f32,
994        confirm_method: UiConfirm,
995        notify_on: UiNotify,
996    ) -> Bool32T;
997    pub fn ui_vslider_16(
998        id: *const c_ushort,
999        value: *mut f32,
1000        min: f32,
1001        max: f32,
1002        step: f32,
1003        height: f32,
1004        confirm_method: UiConfirm,
1005        notify_on: UiNotify,
1006    ) -> Bool32T;
1007    pub fn ui_vslider_f64(
1008        id: *const c_char,
1009        value: *mut f64,
1010        min: f64,
1011        max: f64,
1012        step: f64,
1013        height: f32,
1014        confirm_method: UiConfirm,
1015        notify_on: UiNotify,
1016    ) -> Bool32T;
1017    pub fn ui_vslider_f64_16(
1018        id: *const c_ushort,
1019        value: *mut f64,
1020        min: f64,
1021        max: f64,
1022        step: f64,
1023        height: f32,
1024        confirm_method: UiConfirm,
1025        notify_on: UiNotify,
1026    ) -> Bool32T;
1027    pub fn ui_vslider_at(
1028        id: *const c_char,
1029        value: *mut f32,
1030        min: f32,
1031        max: f32,
1032        step: f32,
1033        window_relative_pos: Vec3,
1034        size: Vec2,
1035        confirm_method: UiConfirm,
1036        notify_on: UiNotify,
1037    ) -> Bool32T;
1038    pub fn ui_vslider_at_16(
1039        id: *const c_ushort,
1040        value: *mut f32,
1041        min: f32,
1042        max: f32,
1043        step: f32,
1044        window_relative_pos: Vec3,
1045        size: Vec2,
1046        confirm_method: UiConfirm,
1047        notify_on: UiNotify,
1048    ) -> Bool32T;
1049    pub fn ui_vslider_at_f64(
1050        id: *const c_char,
1051        value: *mut f64,
1052        min: f64,
1053        max: f64,
1054        step: f64,
1055        window_relative_pos: Vec3,
1056        size: Vec2,
1057        confirm_method: UiConfirm,
1058        notify_on: UiNotify,
1059    ) -> Bool32T;
1060    pub fn ui_vslider_at_f64_16(
1061        id: *const c_ushort,
1062        value: *mut f64,
1063        min: f64,
1064        max: f64,
1065        step: f64,
1066        window_relative_pos: Vec3,
1067        size: Vec2,
1068        confirm_method: UiConfirm,
1069        notify_on: UiNotify,
1070    ) -> Bool32T;
1071    pub fn ui_input(
1072        id: *const c_char,
1073        buffer: *mut c_char,
1074        buffer_size: i32,
1075        size: Vec2,
1076        type_: TextContext,
1077    ) -> Bool32T;
1078    pub fn ui_input_at(
1079        id: *const c_char,
1080        buffer: *mut c_char,
1081        buffer_size: i32,
1082        window_relative_pos: Vec3,
1083        size: Vec2,
1084        type_: TextContext,
1085    ) -> Bool32T;
1086    pub fn ui_input_16(
1087        id: *const c_ushort,
1088        buffer: *mut c_ushort,
1089        buffer_size: i32,
1090        size: Vec2,
1091        type_: TextContext,
1092    ) -> Bool32T;
1093    pub fn ui_input_at_16(
1094        id: *const c_ushort,
1095        buffer: *mut c_ushort,
1096        buffer_size: i32,
1097        window_relative_pos: Vec3,
1098        size: Vec2,
1099        type_: TextContext,
1100    ) -> Bool32T;
1101    pub fn ui_image(image: SpriteT, size: Vec2);
1102    pub fn ui_model(model: ModelT, ui_size: Vec2, model_scale: f32);
1103    pub fn ui_model_at(model: ModelT, start: Vec3, size: Vec3, color: Color128);
1104    pub fn ui_hprogress_bar(percent: f32, width: f32, flip_fil_dir: Bool32T);
1105    pub fn ui_vprogress_bar(percent: f32, height: f32, flip_fil_dir: Bool32T);
1106    pub fn ui_progress_bar_at(
1107        percent: f32,
1108        window_relative_pos: Vec3,
1109        size: Vec2,
1110        bar_direction: UiDir,
1111        flip_fil_dir: Bool32T,
1112    );
1113    pub fn ui_hseparator();
1114    // Deprecated : pub fn ui_space(space: f32);
1115    pub fn ui_hspace(horizontal_space: f32);
1116    pub fn ui_vspace(vertical_space: f32);
1117    pub fn ui_handle_begin(
1118        text: *const c_char,
1119        movement: *mut Pose,
1120        handle: Bounds,
1121        draw: Bool32T,
1122        move_type: UiMove,
1123        allowed_gestures: UiGesture,
1124    ) -> Bool32T;
1125    pub fn ui_handle_begin_16(
1126        text: *const c_ushort,
1127        movement: *mut Pose,
1128        handle: Bounds,
1129        draw: Bool32T,
1130        move_type: UiMove,
1131        allowed_gestures: UiGesture,
1132    ) -> Bool32T;
1133    pub fn ui_handle_end();
1134    pub fn ui_window_begin(text: *const c_char, pose: *mut Pose, size: Vec2, window_type: UiWin, move_type: UiMove);
1135    pub fn ui_window_begin_16(
1136        text: *const c_ushort,
1137        pose: *mut Pose,
1138        size: Vec2,
1139        window_type: UiWin,
1140        move_type: UiMove,
1141    );
1142    pub fn ui_window_end();
1143    pub fn ui_panel_at(start: Vec3, size: Vec2, padding: UiPad);
1144    pub fn ui_panel_begin(padding: UiPad);
1145    pub fn ui_panel_end();
1146}
1147
1148impl Ui {
1149    /// StereoKit will generate a color palette from this gamma space color, and use it to skin the UI! To explicitly
1150    /// adjust individual theme colors, see Ui::set_theme_color.
1151    ///  <https://stereokit.net/Pages/StereoKit/UI/ColorScheme.html>
1152    ///
1153    /// see also [`ui_set_color`] [`Ui::set_theme_color`]
1154    /// ### Examples
1155    /// ```
1156    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1157    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, util::named_colors};
1158    ///
1159    /// let mut window_pose = Pose::new(
1160    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
1161    ///
1162    /// Ui::color_scheme(named_colors::GREEN);
1163    ///
1164    /// let mut agree = true;
1165    /// let mut scaling = 0.75;
1166    /// filename_scr = "screenshots/ui_color_scheme.jpeg";
1167    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1168    ///     Ui::window_begin("Give a value", &mut window_pose, None, None, None);
1169    ///     Ui::panel_begin(None);
1170    ///     Ui::hslider("scaling", &mut scaling, 0.0, 1.0, Some(0.05), Some(0.14), None, None);
1171    ///     Ui::panel_end();
1172    ///     Ui::toggle("I agree!",&mut agree, None);
1173    ///     Ui::same_line();
1174    ///     if Ui::button("Exit", None) {
1175    ///        sk.quit(None);
1176    ///     }
1177    ///     Ui::window_end();
1178    /// );
1179    /// ```
1180    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_color_scheme.jpeg" alt="screenshot" width="200">
1181    pub fn color_scheme(color: impl Into<Color128>) {
1182        unsafe { ui_set_color(color.into()) };
1183    }
1184
1185    /// Enables or disables the far ray grab interaction for Handle elements like the Windows. It can be enabled and
1186    /// disabled for individual UI elements, and if this remains disabled at the start of the next frame, then the
1187    /// hand ray indicators will not be visible. This is enabled by default.
1188    /// <https://stereokit.net/Pages/StereoKit/UI/EnableFarInteract.html>
1189    ///
1190    /// see also [`ui_enable_far_interact`] [`Ui::get_enable_far_interact`]
1191    /// ### Examples
1192    /// ```
1193    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1194    /// use stereokit_rust::ui::Ui;
1195    ///
1196    /// assert_eq!(Ui::get_enable_far_interact(), true);
1197    ///
1198    /// Ui::enable_far_interact(false);
1199    /// assert_eq!(Ui::get_enable_far_interact(), false);
1200    /// ```
1201    pub fn enable_far_interact(enable: bool) {
1202        unsafe { ui_enable_far_interact(enable as Bool32T) };
1203    }
1204
1205    /// UI sizing and layout settings.
1206    /// <https://stereokit.net/Pages/StereoKit/UI/Settings.html>
1207    ///
1208    /// see also [`ui_settings`] [`Ui::get_settings`]
1209    /// ### Examples
1210    /// ```
1211    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1212    /// use stereokit_rust::ui::{Ui, UiSettings};
1213    ///
1214    /// let settings = Ui::get_settings();
1215    /// assert_eq!(settings.margin, 0.010000001);
1216    /// assert_eq!(settings.padding, 0.010000001);
1217    /// assert_eq!(settings.gutter, 0.010000001);
1218    /// assert_eq!(settings.depth, 0.010000001);
1219    /// assert_eq!(settings.rounding, 0.0075000003);
1220    /// assert_eq!(settings.backplate_depth, 0.4);
1221    /// assert_eq!(settings.backplate_border, 0.0005);
1222    /// assert_eq!(settings.separator_scale, 0.4);
1223    ///
1224    /// let new_settings = UiSettings {
1225    ///     margin: 0.005,
1226    ///     padding: 0.005,
1227    ///     gutter: 0.005,
1228    ///     depth: 0.015,
1229    ///     rounding: 0.004,
1230    ///     backplate_depth: 0.6,
1231    ///     backplate_border: 0.002,
1232    ///     separator_scale: 0.6,
1233    /// };
1234    /// Ui::settings(new_settings);
1235    /// let settings = Ui::get_settings();
1236    /// assert_eq!(settings.margin, 0.005);
1237    /// assert_eq!(settings.padding, 0.005);
1238    /// assert_eq!(settings.gutter, 0.005);
1239    /// assert_eq!(settings.depth, 0.015);
1240    /// assert_eq!(settings.rounding, 0.004);
1241    /// assert_eq!(settings.backplate_depth, 0.6);
1242    /// assert_eq!(settings.backplate_border, 0.002);
1243    /// assert_eq!(settings.separator_scale, 0.6);
1244    /// ```
1245    pub fn settings(settings: UiSettings) {
1246        unsafe { ui_settings(settings) }
1247    }
1248
1249    /// Shows or hides the collision volumes of the UI! This is for debug purposes, and can help identify visible and
1250    /// invisible collision issues.
1251    /// <https://stereokit.net/Pages/StereoKit/UI/ui_show_volumes.html>
1252    ///
1253    /// see also [`ui_show_volumes`]
1254    /// ### Examples
1255    /// ```
1256    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1257    /// use stereokit_rust::ui::Ui;
1258    ///
1259    /// Ui::show_volumes(true);
1260    /// ```
1261    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_show_volumes.jpeg" alt="screenshot" width="200">
1262    pub fn show_volumes(show: bool) {
1263        unsafe { ui_show_volumes(show as Bool32T) };
1264    }
1265
1266    /// This is the UiMove that is provided to UI windows that StereoKit itself manages, such as the fallback
1267    /// filepicker and soft keyboard.
1268    /// <https://stereokit.net/Pages/StereoKit/UI/SystemMoveType.html>
1269    ///
1270    /// see also [`ui_system_set_move_type`] [`Ui::get_system_move_type`]
1271    /// ### Examples
1272    /// ```
1273    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1274    /// use stereokit_rust::ui::{Ui, UiMove};
1275    ///
1276    /// assert_eq!(Ui::get_system_move_type(), UiMove::FaceUser);
1277    ///
1278    /// Ui::system_move_type(UiMove::Exact);
1279    /// assert_eq!(Ui::get_system_move_type(), UiMove::Exact);
1280    /// ```
1281    pub fn system_move_type(move_type: UiMove) {
1282        unsafe { ui_system_set_move_type(move_type) };
1283    }
1284
1285    /// A pressable button! A button will expand to fit the text provided to it, vertically and horizontally. Text is
1286    /// re-used as the id. Will return true only on the first frame it is pressed!
1287    /// <https://stereokit.net/Pages/StereoKit/UI/Button.html>
1288    /// * `text` - Text to display on the button and id for tracking element state. MUST be unique within current
1289    ///   hierarchy.
1290    /// * `size` - The layout size for this element in Hierarchy space. If an axis is left as zero, it will be
1291    ///   auto-calculated. For X this is the remaining width of the current layout, and for Y this is Ui::get_line_height.
1292    ///
1293    /// Returns true if the button was pressed this frame.
1294    /// see also [`ui_button`] [`ui_button_sz`] [`Ui::button_at`]  [`Ui::button_behavior`]
1295    /// ### Examples
1296    /// ```
1297    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1298    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
1299    ///
1300    /// let mut window_pose = Pose::new(
1301    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
1302    ///
1303    /// let mut button = 0;
1304    /// filename_scr = "screenshots/ui_button.jpeg";
1305    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1306    ///     Ui::window_begin("Press a button", &mut window_pose, None, None, None);
1307    ///     if Ui::button("1", None) {button = 1}
1308    ///     Ui::same_line();
1309    ///     if Ui::button("2", Some([0.025, 0.025].into())) {button = 2}
1310    ///     if Ui::button("3", Some([0.04, 0.04].into())) {button = 3}
1311    ///     if Ui::button_at("4", [-0.01, -0.01, 0.005],[0.05, 0.05]) {button = 4}
1312    ///     if Ui::button_at("5", [-0.04, -0.08, 0.005],[0.03, 0.03]) {button = 5}
1313    ///     Ui::window_end();
1314    /// );
1315    /// ```
1316    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_button.jpeg" alt="screenshot" width="200">
1317    pub fn button(text: impl AsRef<str>, size: Option<Vec2>) -> bool {
1318        let cstr = CString::new(text.as_ref()).unwrap();
1319        match size {
1320            Some(size) => unsafe { ui_button_sz(cstr.as_ptr(), size) != 0 },
1321            None => unsafe { ui_button(cstr.as_ptr()) != 0 },
1322        }
1323    }
1324
1325    /// A variant of Ui::button that doesn’t use the layout system, and instead goes exactly where you put it.
1326    /// <https://stereokit.net/Pages/StereoKit/UI/ButtonAt.html>
1327    /// * `text` - Text to display on the button and id for tracking element state. MUST be unique within current
1328    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
1329    ///   hierarchy.
1330    /// * `size` - The layout size for this element in Hierarchy space.
1331    ///
1332    /// Returns true if the button was pressed this frame.
1333    /// see also [`ui_button_at`] [`Ui::button_behavior`]
1334    /// see example in [`Ui::button`]
1335    pub fn button_at(text: impl AsRef<str>, top_left_corner: impl Into<Vec3>, size: impl Into<Vec2>) -> bool {
1336        let cstr = CString::new(text.as_ref()).unwrap();
1337
1338        unsafe { ui_button_at(cstr.as_ptr(), top_left_corner.into(), size.into()) != 0 }
1339    }
1340
1341    /// This is the core functionality of StereoKit’s buttons, without any of the rendering parts! If you’re trying to
1342    /// create your own pressable UI elements, or do more extreme customization of the look and feel of UI elements,
1343    /// then this function will provide a lot of complex pressing functionality for you!
1344    /// <https://stereokit.net/Pages/StereoKit/UI/ButtonBehavior.html>
1345    /// * `window_relative_pos` - The layout position of the pressable area.
1346    /// * `size` - The size of the pressable area.
1347    /// * `id` - The id for this pressable element to track its state with.
1348    /// * `out_finger_offset` - This is the current distance of the finger, within the pressable volume, from the
1349    ///   bottom of the button.
1350    /// * `out_button_state` - This is the current frame’s “active” state for the button.
1351    /// * `out_focus_state` - This is the current frame’s “focus” state for the button.
1352    /// * `out_hand` - Id of the hand that interacted with the button. This will be -1 if no interaction has occurred.
1353    ///
1354    /// see also [`ui_button_behavior`] [`Ui::button_behavior_depth`]
1355    /// ### Examples
1356    /// ```
1357    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1358    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, system::BtnState};
1359    ///
1360    /// let mut window_pose = Pose::new(
1361    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
1362    ///
1363    /// let mut out_button_state = BtnState::empty();
1364    /// let mut out_focus_state = BtnState::empty();
1365    /// let mut out_finger_offset = 0.0;
1366    /// filename_scr = "screenshots/ui_button_behavior.jpeg";
1367    /// test_steps!( // !!!! Get a proper main loop !!!!
1368    ///     Ui::window_begin("I'm a button", &mut window_pose, None, None, None);
1369    ///     Ui::button_behavior([0.0, 0.0, 0.005],[0.05, 0.05], "Button1",
1370    ///                         &mut out_finger_offset, &mut out_button_state,
1371    ///                         &mut out_focus_state, None);
1372    ///     if out_button_state.is_just_inactive() {
1373    ///        println!("Button1 pressed");
1374    ///     }
1375    ///     Ui::window_end();
1376    /// );
1377    /// ```
1378    pub fn button_behavior(
1379        window_relative_pos: impl Into<Vec3>,
1380        size: impl Into<Vec2>,
1381        id: impl AsRef<str>,
1382        out_finger_offset: &mut f32,
1383        out_button_state: &mut BtnState,
1384        out_focus_state: &mut BtnState,
1385        out_hand: Option<&mut i32>,
1386    ) {
1387        let id_hash = Ui::stack_hash(id);
1388        let mut nevermind = 0;
1389        let out_opt_hand = out_hand.unwrap_or(&mut nevermind);
1390
1391        unsafe {
1392            ui_button_behavior(
1393                window_relative_pos.into(),
1394                size.into(),
1395                id_hash,
1396                out_finger_offset,
1397                out_button_state,
1398                out_focus_state,
1399                out_opt_hand,
1400            )
1401        }
1402    }
1403
1404    /// This is the core functionality of StereoKit’s buttons, without any of the rendering parts! If you’re trying to
1405    /// create your own pressable UI elements, or do more extreme customization of the look and feel of UI elements,
1406    /// then this function will provide a lot of complex pressing functionality for you! This overload allows for
1407    /// customizing the depth of the button, which otherwise would use UiSettings.depth for its values.
1408    /// <https://stereokit.net/Pages/StereoKit/UI/ButtonBehavior.html>
1409    /// * hand - Id of the hand that interacted with the button. This will be -1 if no interaction has occurred.
1410    ///
1411    /// see also [`ui_button_behavior_depth`]
1412    /// * `window_relative_pos` - The layout position of the pressable area.
1413    /// * `size` - The size of the pressable area.
1414    /// * `id` - The id for this pressable element to track its state with.
1415    /// * `button_depth` - This is the z axis depth of the pressable area.
1416    /// * `button_activation_depth` - This is the current distance of the finger, within the pressable volume, from the
1417    ///   bottom of the button.
1418    /// * `out_finger_offset` - This is the current distance of the finger, within the pressable volume, from the
1419    ///   bottom of the button.
1420    /// * `out_button_state` - This is the current frame’s “active” state for the button.
1421    /// * `out_focus_state` - This is the current frame’s “focus” state for the button.
1422    ///
1423    /// see also [`ui_button_behavior_depth`] [`Ui::button_behavior`]
1424    /// ### Examples
1425    /// ```
1426    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1427    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, system::BtnState};
1428    ///
1429    /// let mut window_pose = Pose::new(
1430    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
1431    ///
1432    /// let mut out_button_state = BtnState::empty();
1433    /// let mut out_focus_state = BtnState::empty();
1434    /// let mut out_finger_offset = 0.0;
1435    /// test_steps!( // !!!! Get a proper main loop !!!!
1436    ///     Ui::window_begin("I'm a button", &mut window_pose, None, None, None);
1437    ///     Ui::button_behavior_depth([0.0, 0.0, 0.005],[0.05, 0.05], "Button1", 0.01, 0.005,
1438    ///                         &mut out_finger_offset, &mut out_button_state,
1439    ///                         &mut out_focus_state, None);
1440    ///     if out_button_state.is_just_inactive() {
1441    ///        println!("Button1 pressed");
1442    ///     }
1443    ///     Ui::window_end();
1444    /// );
1445    /// ```
1446    #[allow(clippy::too_many_arguments)]
1447    pub fn button_behavior_depth(
1448        top_left_corner: impl Into<Vec3>,
1449        size: impl Into<Vec2>,
1450        id: impl AsRef<str>,
1451        button_depth: f32,
1452        button_activation_depth: f32,
1453        out_finger_offset: &mut f32,
1454        out_button_state: &mut BtnState,
1455        out_focus_state: &mut BtnState,
1456        out_opt_hand: Option<&mut i32>,
1457    ) {
1458        let id_hash = Ui::stack_hash(id);
1459        let mut nevermind = 0;
1460        let out_opt_hand = out_opt_hand.unwrap_or(&mut nevermind);
1461
1462        unsafe {
1463            ui_button_behavior_depth(
1464                top_left_corner.into(),
1465                size.into(),
1466                id_hash,
1467                button_depth,
1468                button_activation_depth,
1469                out_finger_offset,
1470                out_button_state,
1471                out_focus_state,
1472                out_opt_hand,
1473            )
1474        }
1475    }
1476
1477    /// This is the core functionality of StereoKit's slider elements, without any of the rendering parts! If you're
1478    /// trying to create your own sliding UI elements, or do more extreme customization of the look and feel of slider
1479    /// UI elements, then this function will provide a lot of complex pressing functionality for you
1480    /// <https://stereokit.net/Pages/StereoKit/UI/SliderBehavior.html>
1481    /// * `window_relative_pos` - The layout position of the pressable area.
1482    /// * `size` - The size of the pressable area.
1483    /// * `id` - The id for this pressable element to track its state with.
1484    /// * `value` - The value that the slider will store slider state in.
1485    /// * `min` - The minimum value the slider can set, left side of the slider.
1486    /// * `max` - The maximum value the slider can set, right side of the slider.
1487    /// * `button_size_visual` - This is the visual size of the element representing the touchable area of the slider.
1488    ///   This is used to calculate the center of the button's placement without going outside the provided bounds.
1489    /// * `button_size_interact` - The size of the interactive touch element of the slider. Set this to zero to use the
1490    ///   entire area as a touchable surface.
1491    /// * `confirm_method` - How should the slider be activated? Default Push will be a push-button the user must press
1492    ///   first, and pinch will be a tab that the user must pinch and drag around.
1493    /// * `data` - This is data about the slider interaction, you can use this for visualizing the slider behavior, or
1494    ///   reacting to its events.
1495    ///
1496    /// see also [`ui_slider_behavior`]
1497    /// ### Examples
1498    /// ```
1499    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1500    /// use stereokit_rust::{ui::{Ui, UiSliderData, UiVisual}, maths::{Vec2, Vec3, Pose},
1501    ///                      system::BtnState};
1502    ///
1503    /// let mut window_pose = Pose::new(
1504    ///     [0.01, 0.07, 0.90], Some([0.0, 185.0, 0.0].into()));
1505    ///
1506    /// let depth = Ui::get_settings().depth;
1507    /// let size = Vec2::new(0.18, 0.11);
1508    /// let btn_height = Ui::get_line_height() * 0.5;
1509    /// let btn_size = Vec3::new(btn_height, btn_height, depth);
1510    ///
1511    /// let mut slider_pt = Vec2::new(0.25, 0.65);
1512    /// let id_slider = "touch panel";
1513    /// let id_slider_hash = Ui::stack_hash(&id_slider);
1514    ///
1515    /// filename_scr = "screenshots/ui_slider_behavior.jpeg";
1516    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1517    ///     let prev = slider_pt;
1518    ///     let mut slider = UiSliderData::default();
1519    ///
1520    ///     Ui::window_begin("I'm a slider", &mut window_pose, None, None, None);
1521    ///     let bounds = Ui::layout_reserve(size, false, depth);
1522    ///     let tlb = bounds.tlb();
1523    ///     Ui::slider_behavior(tlb , bounds.dimensions.xy(), id_slider_hash, &mut slider_pt,
1524    ///                         Vec2::ZERO, Vec2::ONE, Vec2::ZERO, btn_size.xy(), None, &mut slider);
1525    ///     let focus = Ui::get_anim_focus(id_slider_hash, slider.focus_state, slider.active_state);
1526    ///     Ui::draw_element(UiVisual::SliderLine, None,tlb,
1527    ///                      Vec3::new(bounds.dimensions.x, bounds.dimensions.y, depth * 0.1),
1528    ///                      if slider.focus_state.is_active() { 0.5 } else { 0.0 });
1529    ///     Ui::draw_element(UiVisual::SliderPush, None,
1530    ///                      slider.button_center.xy0() + btn_size.xy0() / 2.0, btn_size, focus);
1531    ///     if slider.active_state.is_just_inactive() {
1532    ///        println!("Slider1 moved");
1533    ///     }
1534    ///     Ui::label(format!("x: {:.2}          y: {:.2}", slider_pt.x, slider_pt.y), None, true);
1535    ///     Ui::window_end();
1536    /// );
1537    /// ```
1538    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_slider_behavior.jpeg" alt="screenshot" width="200">
1539    #[allow(clippy::too_many_arguments)]
1540    pub fn slider_behavior(
1541        window_relative_pos: impl Into<Vec3>,
1542        size: impl Into<Vec2>,
1543        id: IdHashT,
1544        value: &mut Vec2,
1545        min: impl Into<Vec2>,
1546        max: impl Into<Vec2>,
1547        button_size_visual: impl Into<Vec2>,
1548        button_size_interact: impl Into<Vec2>,
1549        confirm_method: Option<UiConfirm>,
1550        data: &mut UiSliderData,
1551    ) {
1552        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
1553        unsafe {
1554            ui_slider_behavior(
1555                window_relative_pos.into(),
1556                size.into(),
1557                id,
1558                value,
1559                min.into(),
1560                max.into(),
1561                button_size_visual.into(),
1562                button_size_interact.into(),
1563                confirm_method,
1564                data,
1565            );
1566        }
1567    }
1568
1569    /// A pressable button accompanied by an image! The button will expand to fit the text provided to it, horizontally.
1570    /// Text is re-used as the id. Will return true only on the first frame it is pressed!
1571    /// <https://stereokit.net/Pages/StereoKit/UI/ButtonImg.html>
1572    /// * `text` - Text to display on the button and id for tracking element state. MUST be unique within current
1573    ///   hierarchy.
1574    /// * `image` - This is the image that will be drawn along with the text. See imageLayout for where the image gets
1575    ///   drawn!
1576    /// * `image_layout` - This enum specifies how the text and image should be laid out on the button. For example,
1577    ///   UiBtnLayout::Left will have the image on the left, and text on the right. If None will have default value of
1578    ///   UiBtnLayout::Left
1579    /// * `size` - The layout size for this element in Hierarchy space. If an axis is left as zero, it will be
1580    ///   auto-calculated. For X this is the remaining width of the current layout, and for Y this is Ui::get_line_height.
1581    /// * `color` - The Sprite’s color will be multiplied by this tint. None will have default value of white.
1582    ///
1583    /// Returns true only on the first frame it is pressed!
1584    /// see also [`ui_button_img`] [`ui_button_img_sz`] [`Ui::button_img_at`]
1585    /// ### Examples
1586    /// ```
1587    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1588    /// use stereokit_rust::{ui::{Ui,UiBtnLayout}, maths::{Vec2, Vec3, Pose},
1589    ///                      sprite::Sprite, util::named_colors};
1590    ///
1591    /// let mut window_pose = Pose::new(
1592    ///     [-0.01, 0.095, 0.88], Some([0.0, 185.0, 0.0].into()));
1593    ///
1594    /// let mut choice = "C";
1595    /// let log_sprite = Sprite::from_file("icons/log_viewer.png", None, None)
1596    ///                               .expect("log_viewer.jpeg should be ok");
1597    /// let scr_sprite = Sprite::from_file("icons/screenshot.png", None, None)
1598    ///                               .expect("screenshot.jpeg should be ok");
1599    /// let app_sprite = Sprite::grid();
1600    ///
1601    /// let fly_sprite = Sprite::from_file("icons/fly_over.png", None, None)
1602    ///                               .expect("fly_over.jpeg should be ok");
1603    /// let close_sprite = Sprite::close();
1604    ///
1605    /// filename_scr = "screenshots/ui_button_img.jpeg";
1606    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1607    ///     Ui::window_begin("Choose a pretty image", &mut window_pose, None, None, None);
1608    ///     if Ui::button_img("Log", &log_sprite, Some(UiBtnLayout::Center),
1609    ///                       Some([0.07, 0.07].into()), Some(named_colors::GOLD.into())) {
1610    ///         choice = "A";
1611    ///     }
1612    ///     if Ui::button_img("screenshot", &scr_sprite, Some(UiBtnLayout::CenterNoText),
1613    ///                       Some([0.07, 0.07].into()), None){
1614    ///         choice = "B";
1615    ///     }
1616    ///     if Ui::button_img("Applications", &app_sprite, Some(UiBtnLayout::Right),
1617    ///                       Some([0.17, 0.04].into()), None) {
1618    ///         choice = "C";
1619    ///     }
1620    ///     if Ui::button_img_at("fly", &fly_sprite, Some(UiBtnLayout::CenterNoText),
1621    ///                          [-0.01, -0.04, 0.0], [0.12, 0.12], Some(named_colors::CYAN.into())) {
1622    ///         choice = "D";
1623    ///     }
1624    ///     if Ui::button_img_at("close", &close_sprite, Some(UiBtnLayout::CenterNoText),
1625    ///                         [-0.08, 0.03, 0.0], [0.05, 0.05], None) {
1626    ///         sk.quit(None);
1627    ///     }
1628    ///     Ui::window_end();
1629    /// );
1630    /// ```
1631    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_button_img.jpeg" alt="screenshot" width="200">
1632    pub fn button_img(
1633        text: impl AsRef<str>,
1634        image: impl AsRef<Sprite>,
1635        image_layout: Option<UiBtnLayout>,
1636        size: Option<Vec2>,
1637        color: Option<Color128>,
1638    ) -> bool {
1639        let cstr = CString::new(text.as_ref()).unwrap();
1640        let image_layout = image_layout.unwrap_or(UiBtnLayout::Left);
1641        let color = color.unwrap_or(Color128::WHITE);
1642        match size {
1643            Some(size) => unsafe {
1644                ui_button_img_sz(cstr.as_ptr(), image.as_ref().0.as_ptr(), image_layout, size, color) != 0
1645            },
1646            None => unsafe { ui_button_img(cstr.as_ptr(), image.as_ref().0.as_ptr(), image_layout, color) != 0 },
1647        }
1648    }
1649
1650    /// A variant of UI::button_img that doesn’t use the layout system, and instead goes exactly where you put it.
1651    /// <https://stereokit.net/Pages/StereoKit/UI/ButtonImgAt.html>
1652    /// * `text` - Text to display on the button and id for tracking element state. MUST be unique within current
1653    ///   hierarchy.
1654    /// * `image` - This is the image that will be drawn along with the text. See imageLayout for where the image gets
1655    ///   drawn!
1656    /// * `image_layout` - This enum specifies how the text and image should be laid out on the button. For example,
1657    ///   UiBtnLayout::Left will have the image on the left, and text on the right. If None will have default value of
1658    ///   UiBtnLayout::Left
1659    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
1660    /// * `size` - The layout size for this element in Hierarchy space.
1661    /// * `color` - The Sprite’s color will be multiplied by this tint. None will have default value of white.
1662    ///
1663    /// Returns true only on the first frame it is pressed!
1664    /// see also [`ui_button_img_at`]
1665    /// see example in [`Ui::button_img`]
1666    pub fn button_img_at(
1667        text: impl AsRef<str>,
1668        image: impl AsRef<Sprite>,
1669        image_layout: Option<UiBtnLayout>,
1670        top_left_corner: impl Into<Vec3>,
1671        size: impl Into<Vec2>,
1672        color: Option<Color128>,
1673    ) -> bool {
1674        let cstr = CString::new(text.as_ref()).unwrap();
1675        let image_layout = image_layout.unwrap_or(UiBtnLayout::Left);
1676        let color = color.unwrap_or(Color128::WHITE);
1677        unsafe {
1678            ui_button_img_at(
1679                cstr.as_ptr(),
1680                image.as_ref().0.as_ptr(),
1681                image_layout,
1682                top_left_corner.into(),
1683                size.into(),
1684                color,
1685            ) != 0
1686        }
1687    }
1688
1689    /// A pressable round button! This button has a square layout,Add commentMore actions and only shows an image, no
1690    /// text. Will return true only on the first frame it is pressed!
1691    /// <https://stereokit.net/Pages/StereoKit/UI/ButtonRound.html>
1692    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
1693    ///   hierarchy.
1694    /// * `image` - An image to display as the face of the button.
1695    /// * `diameter` - The diameter of the button's visual. ThisAdd commentMore actions defaults to the line height.
1696    ///
1697    /// Returns true only on the first frame it is pressed!
1698    /// see also [`ui_button_round`] [`Ui::button_round_at`]
1699    /// ### Examples
1700    /// ```
1701    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1702    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, sprite::Sprite};
1703    ///
1704    /// let mut window_pose = Pose::new(
1705    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
1706    ///
1707    /// let mut button = 0;
1708    /// let close_sprite = Sprite::close();
1709    /// let shift_sprite = Sprite::shift();
1710    /// let list_sprite = Sprite::list();
1711    /// let backspace_sprite = Sprite::backspace();
1712    ///
1713    /// filename_scr = "screenshots/ui_button_round.jpeg";
1714    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1715    ///     Ui::window_begin("Press a round button", &mut window_pose, None, None, None);
1716    ///     if Ui::button_round("1", &close_sprite, 0.07) {button = 1}
1717    ///     Ui::same_line();
1718    ///     if Ui::button_round("2", &shift_sprite, 0.05) {button = 2}
1719    ///     if Ui::button_round_at("3", &list_sprite, [-0.04, 0.04, 0.005], 0.03) {button = 3}
1720    ///     if Ui::button_round_at("4", &backspace_sprite, [-0.04, -0.08, 0.005], 0.04) {button = 4}
1721    ///     Ui::window_end();
1722    /// );
1723    /// ```
1724    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_button_round.jpeg" alt="screenshot" width="200">
1725    pub fn button_round(id: impl AsRef<str>, image: impl AsRef<Sprite>, diameter: f32) -> bool {
1726        let cstr = CString::new(id.as_ref()).unwrap();
1727        unsafe { ui_button_round(cstr.as_ptr(), image.as_ref().0.as_ptr(), diameter) != 0 }
1728    }
1729
1730    /// A variant of Ui::button_round that doesn’t use the layout system, and instead goes exactly where you put it.
1731    /// <https://stereokit.net/Pages/StereoKit/UI/ButtonRoundAt.html>
1732    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
1733    ///   hierarchy.
1734    /// * `image` - An image to display as the face of the button.
1735    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
1736    /// * `diameter` - The diameter of the button’s visual.
1737    ///
1738    /// Returns true only on the first frame it is pressed!
1739    /// see also [`ui_button_round_at`]
1740    /// see example in [`Ui::button_round`]
1741    pub fn button_round_at(
1742        id: impl AsRef<str>,
1743        image: impl AsRef<Sprite>,
1744        top_left_corner: impl Into<Vec3>,
1745        diameter: f32,
1746    ) -> bool {
1747        let cstr = CString::new(id.as_ref()).unwrap();
1748        unsafe { ui_button_round_at(cstr.as_ptr(), image.as_ref().0.as_ptr(), top_left_corner.into(), diameter) != 0 }
1749    }
1750
1751    /// This begins and ends a handle so you can just use its grabbable/moveable functionality! Behaves much like a
1752    /// window, except with a more flexible handle, and no header. You can draw the handle, but it will have no text on
1753    /// it. Returns true for every frame the user is grabbing the handle.
1754    /// <https://stereokit.net/Pages/StereoKit/UI/Handle.html>
1755    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
1756    /// * `pose` - The pose state for the handle! The user will be able to grab this handle and move it around.
1757    ///   The pose is relative to the current hierarchy stack.
1758    /// * `handle` - Size and location of the handle, relative to the pose.
1759    /// * `draw_handle` - Should this function draw the handle for you, or will you draw that yourself?
1760    /// * `move_type` - Describes how the handle will move when dragged around. If None, has default value of UiMove::Exact
1761    /// * `allower_gesture` - Which hand gestures are used for interacting with this Handle? If None, has default value of
1762    ///   UiGesture::Pinch
1763    ///
1764    /// Returns true for every frame the user is grabbing the handle.
1765    /// see also [`ui_handle_begin`] [`ui_handle_end`] [`Ui::handle_begin`]
1766    /// ### Examples
1767    /// ```
1768    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1769    /// use stereokit_rust::{ui::{Ui, UiMove, UiGesture}, maths::{Vec2, Vec3, Pose, Bounds},
1770    ///                      material::Material, mesh::Mesh, util::named_colors};
1771    ///
1772    /// // lets create two handles of the same size:
1773    /// let mut handle_pose1 = Pose::new(
1774    ///     [-0.02, -0.035, 0.92], Some([0.0, 145.0, 0.0].into()));
1775    /// let mut handle_pose2 = Pose::new(
1776    ///     [0.02, 0.035, 0.92], Some([0.0, 145.0, 0.0].into()));
1777    /// let handle_bounds = Bounds::new([0.0, 0.0, 0.0], [0.045, 0.045, 0.045]);
1778    ///
1779    /// let mut material_bound = Material::ui_box();
1780    /// material_bound.color_tint(named_colors::GOLD)
1781    ///               .border_size(0.0025);
1782    /// let cube_bounds  = Mesh::cube();
1783    ///
1784    /// filename_scr = "screenshots/ui_handle.jpeg";
1785    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1786    ///     // Handles are drawn
1787    ///     Ui::handle("Handle1", &mut handle_pose1, handle_bounds, true,
1788    ///                Some(UiMove::FaceUser), Some(UiGesture::Pinch));
1789    ///
1790    ///     // Handles aren't drawn so we draw a cube_bound to show where they are.
1791    ///     Ui::handle("Handle2", &mut handle_pose2, handle_bounds, false,
1792    ///                Some(UiMove::PosOnly), Some(UiGesture::PinchGrip));
1793    ///     cube_bounds.draw(token, &material_bound,
1794    ///                      handle_pose2.to_matrix(Some(handle_bounds.dimensions)), None, None);
1795    /// );
1796    /// ```
1797    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_handle.jpeg" alt="screenshot" width="200">
1798    pub fn handle(
1799        id: impl AsRef<str>,
1800        pose: &mut Pose,
1801        handle: Bounds,
1802        draw_handle: bool,
1803        move_type: Option<UiMove>,
1804        allower_gesture: Option<UiGesture>,
1805    ) -> bool {
1806        let move_type = move_type.unwrap_or(UiMove::Exact);
1807        let allower_gesture = allower_gesture.unwrap_or(UiGesture::Pinch);
1808        let cstr = CString::new(id.as_ref()).unwrap();
1809        let result = unsafe {
1810            ui_handle_begin(cstr.as_ptr(), pose, handle, draw_handle as Bool32T, move_type, allower_gesture) != 0
1811        };
1812        unsafe { ui_handle_end() }
1813        result
1814    }
1815
1816    /// This begins a new UI group with its own layout! Much like a window, except with a more flexible handle, and no
1817    /// header. You can draw the handle, but it will have no text on it. The pose value is always relative to the
1818    /// current hierarchy stack. This call will also push the pose transform onto the hierarchy stack, so any objects
1819    /// drawn up to the corresponding Ui::handle_end() will get transformed by the handle pose. Returns true for every
1820    /// frame the user is grabbing the handle.
1821    /// <https://stereokit.net/Pages/StereoKit/UI/HandleBegin.html>
1822    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
1823    /// * `pose` - The pose state for the handle! The user will be able to grab this handle and move it around.
1824    ///   The pose is relative to the current hierarchy stack.
1825    /// * `handle` - Size and location of the handle, relative to the pose.
1826    /// * `draw_handle` - Should this function draw the handle for you, or will you draw that yourself?
1827    /// * `move_type` - Describes how the handle will move when dragged around. If None, has default value of UiMove::Exact
1828    /// * `allower_gesture` - Which hand gestures are used for interacting with this Handle? If None, has default value of
1829    ///   UiGesture::Pinch
1830    ///
1831    /// Returns true for every frame the user is grabbing the handle.
1832    /// see also [`ui_handle_begin`] [`Ui::handle`]
1833    /// ### Examples
1834    /// ```
1835    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1836    /// use stereokit_rust::{ui::{Ui, UiMove, UiGesture},
1837    ///                      maths::{Vec2, Vec3, Pose, Bounds, Matrix},
1838    ///                      material::Material, mesh::Mesh, util::named_colors, sprite::Sprite};
1839    ///
1840    /// // lets create two handles of the same size:
1841    /// let mut handle_pose1 = Pose::new(
1842    ///     [-0.02, -0.035, 0.92], Some([0.0, 145.0, 0.0].into()));
1843    /// let mut handle_pose2 = Pose::new(
1844    ///     [0.02, 0.035, 0.92], Some([0.0, 145.0, 0.0].into()));
1845    /// let handle_bounds = Bounds::new([0.0, 0.0, 0.0], [0.045, 0.045, 0.045]);
1846    ///
1847    /// let mut material_bound = Material::ui_box();
1848    /// material_bound.color_tint(named_colors::GOLD)
1849    ///               .border_size(0.0025);
1850    /// let cube_bounds  = Mesh::cube();
1851    ///
1852    /// let sphere = Mesh::generate_sphere(0.045, None);
1853    /// let material_sphere = Material::pbr();
1854    ///
1855    /// filename_scr = "screenshots/ui_handle_begin.jpeg";
1856    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1857    ///     // Handles aren't drawn so we draw som cube_bounds to show where they are.
1858    ///     Ui::handle_begin("Handle1", &mut handle_pose1, handle_bounds, false,
1859    ///                Some(UiMove::FaceUser), Some(UiGesture::Pinch));
1860    ///     sphere.draw(token, &material_sphere, Matrix::IDENTITY, None, None);
1861    ///     cube_bounds.draw(token, &material_bound, Matrix::s(handle_bounds.dimensions), None, None);
1862    ///     Ui::handle_end();
1863    ///
1864    ///     Ui::handle_begin("Handle2", &mut handle_pose2, handle_bounds, false,
1865    ///                Some(UiMove::PosOnly), Some(UiGesture::PinchGrip));
1866    ///     sphere.draw(token, &material_sphere, Matrix::IDENTITY, None, None);
1867    ///     cube_bounds.draw(token, &material_bound, Matrix::s(handle_bounds.dimensions), None, None);
1868    ///     Ui::handle_end();
1869    /// );
1870    /// ```
1871    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_handle_begin.jpeg" alt="screenshot" width="200">
1872    pub fn handle_begin(
1873        id: impl AsRef<str>,
1874        pose: &mut Pose,
1875        handle: Bounds,
1876        draw_handle: bool,
1877        move_type: Option<UiMove>,
1878        allower_gesture: Option<UiGesture>,
1879    ) -> bool {
1880        let move_type = move_type.unwrap_or(UiMove::Exact);
1881        let allower_gesture = allower_gesture.unwrap_or(UiGesture::Pinch);
1882        let cstr = CString::new(id.as_ref()).unwrap();
1883        unsafe { ui_handle_begin(cstr.as_ptr(), pose, handle, draw_handle as Bool32T, move_type, allower_gesture) != 0 }
1884    }
1885
1886    /// Finishes a handle! Must be called after [`Ui::handle_begin`] and all elements have been drawn. Pops the pose
1887    /// transform pushed by Ui::handle_begin() from the hierarchy stack.
1888    /// <https://stereokit.net/Pages/StereoKit/UI/HandleEnd.html>
1889    ///
1890    /// see also [`ui_handle_end`]
1891    /// see example in [`Ui::handle_begin`]
1892    pub fn handle_end() {
1893        unsafe { ui_handle_end() };
1894    }
1895
1896    /// This draws a line horizontally across the current layout. Makes a good separator between sections of UI!
1897    /// <https://stereokit.net/Pages/StereoKit/UI/HSeparator.html>
1898    ///
1899    /// see also [`ui_hseparator`]
1900    /// ### Examples
1901    /// ```
1902    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1903    /// use stereokit_rust::{ui::Ui, maths::{Vec2,  Pose}, system::TextStyle,
1904    ///                      util::named_colors, font::Font};
1905    ///
1906    /// let mut window_pose = Pose::new(
1907    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
1908    ///
1909    /// let font = Font::default();
1910    /// let mut style_big    = TextStyle::from_font(&font, 0.002, named_colors::CYAN);
1911    /// style_big.layout_height(0.018);
1912    /// let mut style_medium = TextStyle::from_font(&font, 0.002, named_colors::RED);
1913    /// style_medium.layout_height(0.010);
1914    /// let mut style_small  = TextStyle::from_font(&font, 0.002, named_colors::GOLD);
1915    /// style_small.layout_height(0.006);
1916    ///
1917    /// filename_scr = "screenshots/ui_hseparator.jpeg";
1918    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1919    ///     Ui::window_begin("Separators", &mut window_pose, Some([0.17, 0.0].into()), None, None);
1920    ///     Ui::push_text_style(style_big);
1921    ///     Ui::text("The first part", None, None, None, None, None, None);
1922    ///     Ui::hseparator();
1923    ///     Ui::pop_text_style();
1924    ///     Ui::push_text_style(style_medium);
1925    ///     Ui::text("The second part", None, None, None, None, None, None);
1926    ///     Ui::hseparator();
1927    ///     Ui::pop_text_style();
1928    ///     Ui::push_text_style(style_small);
1929    ///     Ui::text("The third part", None, None, None, None, None, None);
1930    ///     Ui::hseparator();
1931    ///     Ui::pop_text_style();
1932    ///     Ui::window_end();
1933    /// );
1934    /// ```
1935    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_hseparator.jpeg" alt="screenshot" width="200">
1936    pub fn hseparator() {
1937        unsafe { ui_hseparator() };
1938    }
1939
1940    /// A vertical slider element! You can stick your finger in it, and slide the value up and down.
1941    /// <https://stereokit.net/Pages/StereoKit/UI/HSlider.html>
1942    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
1943    /// * `out_value` - The value that the slider will store slider state in.
1944    /// * `min` - The minimum value the slider can set, left side of the slider.
1945    /// * `max` - The maximum value the slider can set, right side of the slider.
1946    /// * `step` - Locks the value to increments of step. Starts at min, and increments by step. None is default 0
1947    ///   and means "don't lock to increments".
1948    /// * `width` - Physical width of the slider on the window. None is default 0 will fill the remaining amount of
1949    ///   window space.
1950    /// * `confirm_method` - How should the slider be activated? None is default Push will be a push-button the user
1951    ///   must press first, and pinch will be a tab that the user must pinch and drag around.
1952    /// * `notify_on` - Allows you to modify the behavior of the return value. None is default UiNotify::Change.
1953    ///
1954    /// Returns new value of the slider if it has changed during this step.
1955    /// see also [`ui_hslider`] [`Ui::hslider_f64`] [`Ui::hslider_at`] [`Ui::hslider_at_f64`]
1956    /// ### Examples
1957    /// ```
1958    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1959    /// use stereokit_rust::{ui::{Ui, UiConfirm, UiNotify}, maths::{Vec2, Vec3, Pose}};
1960    ///
1961    /// let mut window_pose = Pose::new(
1962    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
1963    ///
1964    /// let mut scaling1 = 0.15;
1965    /// let mut scaling2 = 0.50f64;
1966    /// let mut scaling3 = 0.0;
1967    /// let mut scaling4 = 0.85;
1968    ///
1969    /// filename_scr = "screenshots/ui_hslider.jpeg";
1970    /// test_screenshot!( // !!!! Get a proper main loop !!!!
1971    ///     Ui::window_begin("VSlider", &mut window_pose, None, None, None);
1972    ///     Ui::hslider(    "scaling1", &mut scaling1, 0.0, 1.0, Some(0.05), Some(0.10),
1973    ///                     None, None);
1974    ///     Ui::hslider_f64("scaling2", &mut scaling2, 0.0, 1.0, None, Some(0.12),
1975    ///                     Some(UiConfirm::Pinch), None);
1976    ///     Ui::hslider_at( "scaling3", &mut scaling3, 0.0, 1.0, None,
1977    ///                     [0.0, 0.0, 0.0], [0.08, 0.02],
1978    ///                     None, Some(UiNotify::Finalize));
1979    ///     if let Some(new_value) = Ui::hslider_at_f64(
1980    ///                     "scaling4", &mut scaling4, 0.0, 1.0, None,
1981    ///                     [0.07, -0.085, 0.0], [0.15, 0.036],
1982    ///                     Some(UiConfirm::VariablePinch), None) {
1983    ///         if new_value == 1.0 {
1984    ///             Log::info("scaling4 is at max");
1985    ///         }
1986    ///     }
1987    ///     Ui::window_end();
1988    /// );
1989    /// ```
1990    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_hslider.jpeg" alt="screenshot" width="200">
1991    #[allow(clippy::too_many_arguments)]
1992    pub fn hslider(
1993        id: impl AsRef<str>,
1994        out_value: &mut f32,
1995        min: f32,
1996        max: f32,
1997        step: Option<f32>,
1998        width: Option<f32>,
1999        confirm_method: Option<UiConfirm>,
2000        notify_on: Option<UiNotify>,
2001    ) -> Option<f32> {
2002        let cstr = CString::new(id.as_ref()).unwrap();
2003        let step = step.unwrap_or(0.0);
2004        let width = width.unwrap_or(0.0);
2005        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
2006        let notify_on = notify_on.unwrap_or(UiNotify::Change);
2007        match unsafe { ui_hslider(cstr.as_ptr(), out_value, min, max, step, width, confirm_method, notify_on) != 0 } {
2008            true => Some(*out_value),
2009            false => None,
2010        }
2011    }
2012
2013    /// A vertical slider element! You can stick your finger in it, and slide the value up and down.
2014    /// <https://stereokit.net/Pages/StereoKit/UI/HSlider.html>
2015    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
2016    /// * `out_value` - The value that the slider will store slider state in.
2017    /// * `min` - The minimum value the slider can set, left side of the slider.
2018    /// * `max` - The maximum value the slider can set, right side of the slider.
2019    /// * `step` - Locks the value to increments of step. Starts at min, and increments by step. None is default 0
2020    ///   and means "don't lock to increments".
2021    /// * `width` - Physical width of the slider on the window. None is default 0 will fill the remaining amount of
2022    ///   window space.
2023    /// * `confirm_method` - How should the slider be activated? None is default Push will be a push-button the user
2024    ///   must press first, and pinch will be a tab that the user must pinch and drag around.
2025    /// * `notify_on` - Allows you to modify the behavior of the return value. None is default UiNotify::Change.
2026    ///
2027    /// Returns new value of the slider if it has changed during this step.
2028    /// see also [`ui_hslider_f64`] [`Ui::hslider`] [`Ui::hslider_at`] [`Ui::hslider_at_f64`]
2029    /// see example in [`Ui::hslider`]
2030    #[allow(clippy::too_many_arguments)]
2031    pub fn hslider_f64(
2032        id: impl AsRef<str>,
2033        out_value: &mut f64,
2034        min: f64,
2035        max: f64,
2036        step: Option<f64>,
2037        width: Option<f32>,
2038        confirm_method: Option<UiConfirm>,
2039        notify_on: Option<UiNotify>,
2040    ) -> Option<f64> {
2041        let cstr = CString::new(id.as_ref()).unwrap();
2042        let step = step.unwrap_or(0.0);
2043        let width = width.unwrap_or(0.0);
2044        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
2045        let notify_on = notify_on.unwrap_or(UiNotify::Change);
2046        match unsafe { ui_hslider_f64(cstr.as_ptr(), out_value, min, max, step, width, confirm_method, notify_on) != 0 }
2047        {
2048            true => Some(*out_value),
2049            false => None,
2050        }
2051    }
2052
2053    /// A vertical slider element! You can stick your finger in it, and slide the value up and down.
2054    /// <https://stereokit.net/Pages/StereoKit/UI/HSliderAt.html>
2055    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
2056    /// * `out_value` - The value that the slider will store slider state in.
2057    /// * `min` - The minimum value the slider can set, left side of the slider.
2058    /// * `max` - The maximum value the slider can set, right side of the slider.
2059    /// * `step` - Locks the value to increments of step. Starts at min, and increments by step. None is default 0
2060    ///   and means "don't lock to increments".
2061    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
2062    /// * `size` - The layout size for this element in Hierarchy space.
2063    /// * `confirm_method` - How should the slider be activated? None is default Push will be a push-button the user
2064    ///   must press first, and pinch will be a tab that the user must pinch and drag around.
2065    /// * `notify_on` - Allows you to modify the behavior of the return value. None is default UiNotify::Change.
2066    ///
2067    /// Returns new value of the slider if it has changed during this step.
2068    /// see also [`ui_hslider_at`] [`Ui::hslider_f64`] [`Ui::hslider`] [`Ui::hslider_at_f64`]
2069    /// see example in [`Ui::hslider`]
2070    #[allow(clippy::too_many_arguments)]
2071    pub fn hslider_at(
2072        id: impl AsRef<str>,
2073        out_value: &mut f32,
2074        min: f32,
2075        max: f32,
2076        step: Option<f32>,
2077        top_left_corner: impl Into<Vec3>,
2078        size: impl Into<Vec2>,
2079        confirm_method: Option<UiConfirm>,
2080        notify_on: Option<UiNotify>,
2081    ) -> Option<f32> {
2082        let cstr = CString::new(id.as_ref()).unwrap();
2083        let step = step.unwrap_or(0.0);
2084        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
2085        let notify_on = notify_on.unwrap_or(UiNotify::Change);
2086        match unsafe {
2087            ui_hslider_at(
2088                cstr.as_ptr(),
2089                out_value,
2090                min,
2091                max,
2092                step,
2093                top_left_corner.into(),
2094                size.into(),
2095                confirm_method,
2096                notify_on,
2097            ) != 0
2098        } {
2099            true => Some(*out_value),
2100            false => None,
2101        }
2102    }
2103
2104    /// A vertical slider element! You can stick your finger in it, and slide the value up and down.
2105    /// <https://stereokit.net/Pages/StereoKit/UI/HSliderAt.html>
2106    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
2107    /// * `out_value` - The value that the slider will store slider state in.
2108    /// * `min` - The minimum value the slider can set, left side of the slider.
2109    /// * `max` - The maximum value the slider can set, right side of the slider.
2110    /// * `step` - Locks the value to increments of step. Starts at min, and increments by step. None is default 0
2111    ///   and means "don't lock to increments".
2112    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
2113    /// * `size` - The layout size for this element in Hierarchy space.
2114    /// * `confirm_method` - How should the slider be activated? None is default Push will be a push-button the user
2115    ///   must press first, and pinch will be a tab that the user must pinch and drag around.
2116    /// * `notify_on` - Allows you to modify the behavior of the return value. None is default UiNotify::Change.
2117    ///
2118    /// Returns new value of the slider if it has changed during this step.
2119    /// see also [`ui_hslider_at_f64`] [`Ui::hslider_f64`] [`Ui::hslider`] [`Ui::hslider_at`]
2120    /// see example in [`Ui::hslider`]
2121    #[allow(clippy::too_many_arguments)]
2122    pub fn hslider_at_f64(
2123        id: impl AsRef<str>,
2124        out_value: &mut f64,
2125        min: f64,
2126        max: f64,
2127        step: Option<f64>,
2128        top_left_corner: impl Into<Vec3>,
2129        size: impl Into<Vec2>,
2130        confirm_method: Option<UiConfirm>,
2131        notify_on: Option<UiNotify>,
2132    ) -> Option<f64> {
2133        let cstr = CString::new(id.as_ref()).unwrap();
2134        let step = step.unwrap_or(0.0);
2135        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
2136        let notify_on = notify_on.unwrap_or(UiNotify::Change);
2137        match unsafe {
2138            ui_hslider_at_f64(
2139                cstr.as_ptr(),
2140                out_value,
2141                min,
2142                max,
2143                step,
2144                top_left_corner.into(),
2145                size.into(),
2146                confirm_method,
2147                notify_on,
2148            ) != 0
2149        } {
2150            true => Some(*out_value),
2151            false => None,
2152        }
2153    }
2154
2155    /// Adds an image to the UI!
2156    /// <https://stereokit.net/Pages/StereoKit/UI/Image.html>
2157    /// * `sprite` - A valid sprite.
2158    /// * `size` - Size in Hierarchy local meters. If one of the components is 0, it’ll be automatically determined from
2159    ///   the other component and the image’s aspect ratio.
2160    ///
2161    /// see also [`ui_image`]
2162    /// ### Examples
2163    /// ```
2164    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2165    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, sprite::Sprite};
2166    ///
2167    /// let mut window_pose = Pose::new(
2168    ///     [0.01, 0.055, 0.92], Some([0.0, 185.0, 0.0].into()));
2169    ///
2170    /// let log_sprite = Sprite::from_file("icons/log_viewer.png", None, None)
2171    ///                               .expect("log_viewer.jpeg should be ok");
2172    /// let scr_sprite = Sprite::from_file("icons/screenshot.png", None, None)
2173    ///                               .expect("screenshot.jpeg should be ok");
2174    /// let app_sprite = Sprite::grid();
2175    ///
2176    /// filename_scr = "screenshots/ui_image.jpeg";
2177    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2178    ///     Ui::window_begin("Images", &mut window_pose, None, None, None);
2179    ///     Ui::image(&log_sprite, [0.03, 0.03]);
2180    ///     Ui::same_line();
2181    ///     Ui::image(&app_sprite, [0.06, 0.06]);
2182    ///     Ui::image(&scr_sprite, [0.05, 0.05]);
2183    ///     Ui::same_line();
2184    ///     Ui::image(&log_sprite, [0.05, 0.05]);
2185    ///     Ui::window_end();
2186    /// );
2187    /// ```
2188    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_image.jpeg" alt="screenshot" width="200">
2189    pub fn image(image: impl AsRef<Sprite>, size: impl Into<Vec2>) {
2190        unsafe { ui_image(image.as_ref().0.as_ptr(), size.into()) };
2191    }
2192
2193    /// This is an input field where users can input text to the app! Selecting it will spawn a virtual keyboard, or act
2194    /// as the keyboard focus. Hitting escape or enter, or focusing another UI element will remove focus from this Input.
2195    /// <https://stereokit.net/Pages/StereoKit/UI/Input.html>
2196    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
2197    /// * `out_value` - The string that will store the Input’s content in.
2198    /// * `size` - The layout size for this element in Hierarchy space.  Zero axes will auto-size. None is full auto-size.
2199    /// * `type_text` - What category of text this Input represents. This may affect what kind of soft keyboard will
2200    ///   be displayed, if one is shown to the user. None has default value of TextContext::Text.
2201    ///
2202    /// Returns the current text in the input field if it has changed, otherwise `None`.
2203    /// see also [`ui_input`] [`Ui::input_at`]
2204    /// ### Examples
2205    /// ```
2206    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2207    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose},
2208    ///                      system::TextContext};
2209    ///
2210    /// let mut window_pose = Pose::new(
2211    ///     [0.01, 0.05, 0.92], Some([0.0, 185.0, 0.0].into()));
2212    ///
2213    /// let mut username = String::from("user");
2214    /// let mut password = String::from("password");
2215    /// let mut zip_code = String::from("97400");
2216    /// let mut pin_code = String::from("123456");
2217    /// filename_scr = "screenshots/ui_input.jpeg";
2218    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2219    ///     Ui::window_begin("Input fields", &mut window_pose, None, None, None);
2220    ///     Ui::input("username1", &mut username, Some([0.15, 0.03].into()), None);
2221    ///     Ui::input("password1", &mut password, None, Some(TextContext::Password));
2222    ///     Ui::input_at("zip code1", &mut zip_code, [0.08, -0.09, 0.0], [0.06, 0.03],
2223    ///                  Some(TextContext::Number));
2224    ///
2225    ///     if let Some(new_value) =
2226    ///         Ui::input_at("pin_code1", &mut pin_code, [0.0, -0.09, 0.0], [0.05, 0.03],
2227    ///                      Some(TextContext::Number)) {
2228    ///         if new_value.is_empty() {
2229    ///             Log::warn("pin_code should not be empty");
2230    ///         }
2231    ///     }
2232    ///     Ui::window_end();
2233    /// );
2234    /// ```
2235    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_input.jpeg" alt="screenshot" width="200">
2236    pub fn input(
2237        id: impl AsRef<str>,
2238        out_value: &mut String,
2239        size: Option<Vec2>,
2240        type_text: Option<TextContext>,
2241    ) -> Option<String> {
2242        let cstr = CString::new(id.as_ref()).unwrap();
2243        let c_value = CString::new(out_value.as_str()).unwrap();
2244        let size = size.unwrap_or(Vec2::ZERO);
2245        let type_text = type_text.unwrap_or(TextContext::Text);
2246        if unsafe {
2247            ui_input(cstr.as_ptr(), c_value.as_ptr() as *mut c_char, out_value.capacity() as i32 + 16, size, type_text)
2248                != 0
2249        } {
2250            match unsafe { CStr::from_ptr(c_value.as_ptr()).to_str() } {
2251                Ok(result) => {
2252                    out_value.clear();
2253                    out_value.push_str(result);
2254                    Some(result.to_owned())
2255                }
2256                Err(_) => None,
2257            }
2258        } else {
2259            None
2260        }
2261    }
2262
2263    /// This is an input field where users can input text to the app! Selecting it will spawn a virtual keyboard, or act
2264    ///  as the keyboard focus. Hitting escape or enter, or focusing another UI element will remove focus from this Input.
2265    /// <https://stereokit.net/Pages/StereoKit/UI/InputAt.html>
2266    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
2267    /// * `out_value` - The string that will store the Input's content in.
2268    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
2269    /// * `size` - The layout size for this element in Hierarchy space.
2270    /// * `type_text` - What category of text this Input represents. This may affect what kind of soft keyboard will
2271    ///   be displayed, if one is shown to the user. None has default value of TextContext::Text.
2272    ///
2273    /// Returns the current text in the input field if it has changed during this step, otherwise `None`.
2274    /// see also [`ui_input_at`]
2275    /// see example in [`Ui::input`]
2276    pub fn input_at(
2277        id: impl AsRef<str>,
2278        out_value: &mut String,
2279        top_left_corner: impl Into<Vec3>,
2280        size: impl Into<Vec2>,
2281        type_text: Option<TextContext>,
2282    ) -> Option<String> {
2283        let cstr = CString::new(id.as_ref()).unwrap();
2284        let c_value = CString::new(out_value.as_str()).unwrap();
2285        let size = size.into();
2286        let type_text = type_text.unwrap_or(TextContext::Text);
2287        if unsafe {
2288            ui_input_at(
2289                cstr.as_ptr(),
2290                c_value.as_ptr() as *mut c_char,
2291                out_value.capacity() as i32 + 16,
2292                top_left_corner.into(),
2293                size,
2294                type_text,
2295            ) != 0
2296        } {
2297            match unsafe { CStr::from_ptr(c_value.as_ptr()).to_str() } {
2298                Ok(result) => {
2299                    out_value.clear();
2300                    out_value.push_str(result);
2301                    Some(result.to_owned())
2302                }
2303                Err(_) => None,
2304            }
2305        } else {
2306            None
2307        }
2308    }
2309
2310    /// Tells if the user is currently interacting with a UI element! This will be true if the hand has an active or
2311    /// focused UI element.
2312    /// TODO: v0.4 These functions use hands instead of interactors, they need replaced!
2313    /// <https://stereokit.net/Pages/StereoKit/UI/IsInteracting.html>
2314    /// * `hand` - The hand to check for interaction.
2315    ///
2316    /// Returns true if the hand has an active or focused UI element. False otherwise.
2317    /// see also [`ui_is_interacting`]
2318    /// ### Examples
2319    /// ```no_run
2320    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2321    /// use stereokit_rust::{ui::Ui, system::Handed};
2322    ///
2323    /// test_steps!( // !!!! Get a proper main loop !!!!
2324    ///     // These are unit tests. no arms, no chocolate.
2325    ///     assert_eq!(Ui::is_interacting(Handed::Right), false);
2326    ///     assert_eq!(Ui::is_interacting(Handed::Left), false);
2327    ///     assert_eq!(Ui::is_interacting(Handed::Max), false);
2328    /// );
2329    /// ```
2330    #[deprecated(
2331        since = "0.4.0",
2332        note = "TODO: These functions use hands instead of interactors, they need replaced!"
2333    )]
2334    pub fn is_interacting(hand: Handed) -> bool {
2335        unsafe { ui_is_interacting(hand) != 0 }
2336    }
2337
2338    /// Adds some text to the layout! Text uses the UI’s current font settings, which can be changed with
2339    /// Ui::push/pop_text_style. Can contain newlines!
2340    /// <https://stereokit.net/Pages/StereoKit/UI/Label.html>
2341    /// * `text` - Label text to display. Can contain newlines! Doesn’t use text as id, so it can be non-unique.
2342    /// * `size` - The layout size for this element in Hierarchy space. If an axis is left as zero, it will be
2343    ///   auto-calculated. For X this is the remaining width of the current layout, and for Y this is
2344    ///   [`Ui::get_line_height`]. If None, both axis will be auto-calculated.
2345    /// * `use_padding` - Should padding be included for positioning this text? Sometimes you just want un-padded text!
2346    ///
2347    /// see also [`ui_label`] [`ui_label_sz`]
2348    /// ### Examples
2349    /// ```
2350    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2351    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
2352    ///
2353    /// let mut window_pose = Pose::new(
2354    ///     [0.01, 0.035, 0.93], Some([0.0, 185.0, 0.0].into()));
2355    ///
2356    /// filename_scr = "screenshots/ui_label.jpeg";
2357    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2358    ///     Ui::window_begin("Labels", &mut window_pose, None, None, None);
2359    ///     Ui::label("Label 1", None, false);
2360    ///     Ui::same_line();
2361    ///     Ui::label("Label 2", Some([0.025, 0.0].into()), false);
2362    ///     Ui::label("Label 3", Some([0.1,   0.01].into()), true);
2363    ///     Ui::label("Label 4", Some([0.0,   0.0045].into()), false);
2364    ///     Ui::window_end();
2365    /// );
2366    /// ```
2367    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_label.jpeg" alt="screenshot" width="200">
2368    pub fn label(text: impl AsRef<str>, size: Option<Vec2>, use_padding: bool) {
2369        let cstr = CString::new(text.as_ref()).unwrap();
2370        match size {
2371            Some(size) => unsafe { ui_label_sz(cstr.as_ptr(), size, use_padding as Bool32T) },
2372            None => unsafe { ui_label(cstr.as_ptr(), use_padding as Bool32T) },
2373        }
2374    }
2375
2376    /// Tells if the hand was involved in the active state of the most recently called UI element using an id. Active
2377    /// state is frequently a single frame in the case of Buttons, but could be many in the case of Sliders or Handles.
2378    /// TODO: v0.4 These functions use hands instead of interactors, they need replaced!
2379    /// <https://stereokit.net/Pages/StereoKit/UI/LastElementHandActive.html>
2380    /// * `hand` - Which hand we’re checking.
2381    ///
2382    /// Returns a BtnState that indicated the hand was “just active” this frame, is currently “active” or if it “just
2383    /// became inactive” this frame.
2384    /// see also [`ui_last_element_hand_active`] [`Ui::get_last_element_active`]
2385    /// ### Examples
2386    /// ```
2387    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2388    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, system::{Handed, BtnState}};
2389    ///
2390    /// let mut window_pose = Pose::new(
2391    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
2392    ///
2393    /// test_steps!( // !!!! Get a proper main loop !!!!
2394    ///     Ui::window_begin("Last Element Hand Active", &mut window_pose, None, None, None);
2395    ///     Ui::button("Button1", None);
2396    ///     let state_hand = Ui::last_element_hand_active(Handed::Right);
2397    ///     let state_element = Ui::get_last_element_active();
2398    ///
2399    ///     assert_eq!( state_hand.is_just_active(), false);
2400    ///     assert_eq!( state_element.is_just_active(), false);
2401    ///     Ui::window_end();
2402    /// );
2403    /// ```
2404    #[deprecated(
2405        since = "0.4.0",
2406        note = "TODO: These functions use hands instead of interactors, they need replaced!"
2407    )]
2408    pub fn last_element_hand_active(hand: Handed) -> BtnState {
2409        unsafe { ui_last_element_hand_active(hand) }
2410    }
2411
2412    /// Tells if the hand was involved in the focus state of the most recently called UI element using an id. Focus
2413    /// occurs when the hand is in or near an element, in such a way that indicates the user may be about to interact
2414    /// with it.
2415    /// TODO: v0.4 These functions use hands instead of interactors, they need replaced!
2416    /// <https://stereokit.net/Pages/StereoKit/UI/LastElementHandFocused.html>
2417    /// * `hand` - Which hand we’re checking.
2418    ///
2419    /// Returns a BtnState that indicated the hand was “just focused” this frame, is currently “focused” or if it “just
2420    /// became focused” this frame.
2421    /// see also [`ui_last_element_hand_focused`] [`Ui::get_last_element_focused`]
2422    /// ### Examples
2423    /// ```
2424    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2425    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, system::{Handed, BtnState}};
2426    ///
2427    /// let mut window_pose = Pose::new(
2428    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
2429    ///
2430    /// test_steps!( // !!!! Get a proper main loop !!!!
2431    ///     Ui::window_begin("Last Element Hand Focuse", &mut window_pose, None, None, None);
2432    ///     Ui::button("Button1", None);
2433    ///     let state_hand = Ui::last_element_hand_focused(Handed::Right);
2434    ///     let state_element = Ui::get_last_element_focused();
2435    ///     assert_eq!( state_hand.is_just_active(), false);
2436    ///     assert_eq!( state_element.is_just_active(), false);
2437    ///     Ui::window_end();
2438    /// );
2439    /// ```
2440    #[deprecated(
2441        since = "0.4.0",
2442        note = "TODO: These functions use hands instead of interactors, they need replaced!"
2443    )]
2444    pub fn last_element_hand_focused(hand: Handed) -> BtnState {
2445        unsafe { ui_last_element_hand_focused(hand) }
2446    }
2447
2448    /// Manually define what area is used for the UI layout. This is in the current Hierarchy’s coordinate space on the
2449    /// X/Y plane.
2450    /// <https://stereokit.net/Pages/StereoKit/UI/LayoutArea.html>
2451    /// * `start` - The top left of the layout area, relative to the current Hierarchy in local meters.
2452    /// * `dimensions` - The size of the layout area from the top left, in local meters.
2453    /// * `add_margin` - If true, the layout area will have a margin added to it.
2454    ///
2455    /// see also [`ui_layout_area`]
2456    /// ### Examples
2457    /// ```
2458    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2459    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, sprite::Sprite};
2460    ///
2461    /// let mut window_pose = Pose::new(
2462    ///     [0.01, 0.055, 0.92], Some([0.0, 185.0, 0.0].into()));
2463    ///
2464    /// let sprite = Sprite::from_file("icons/log_viewer.png", None, None)
2465    ///                       .expect("open_gltf.jpeg should be ok");
2466    ///
2467    /// filename_scr = "screenshots/ui_layout_area.jpeg";
2468    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2469    ///     Ui::window_begin("Layout Area", &mut window_pose, Some([0.15, 0.13].into()), None, None);
2470    ///     Ui::layout_area([0.1, -0.04, 0.0], [0.01, 0.01], true);
2471    ///     Ui::image(&sprite, [0.07, 0.07]);
2472    ///     Ui::layout_area([0.00, -0.01, 0.0], [0.01, 0.01], true);
2473    ///     Ui::label("Text and more", None, false);
2474    ///     Ui::window_end();
2475    /// );
2476    /// ```
2477    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_layout_area.jpeg" alt="screenshot" width="200">
2478    pub fn layout_area(start: impl Into<Vec3>, dimensions: impl Into<Vec2>, add_margin: bool) {
2479        unsafe { ui_layout_area(start.into(), dimensions.into(), add_margin as Bool32T) };
2480    }
2481
2482    /// This pushes a layout rect onto the layout stack. All UI elements using the layout system will now exist inside
2483    /// this layout area! Note that some UI elements such as Windows will already be managing a layout of their own on
2484    /// the stack.
2485    /// <https://stereokit.net/Pages/StereoKit/UI/LayoutPush.html>
2486    /// * `start` - The top left position of the layout. Note that Windows have their origin at the top center, the
2487    ///   left side of a window is X+, and content advances to the X- direction.
2488    /// * `dimensions` - The total size of the layout area. A value of zero means the layout will expand in that axis,
2489    ///   but may prevent certain types of layout “Cuts”.
2490    /// * `add_margin` - Adds a spacing margin to the interior of the layout. Most of the time you won’t need this,
2491    ///   but may be useful when working without a Window.
2492    ///
2493    /// see also [`ui_layout_push`]
2494    /// ### Examples
2495    /// ```
2496    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2497    /// use stereokit_rust::{ui::{Ui, UiCut}, maths::{Vec2, Vec3, Pose}, sprite::Sprite};
2498    ///
2499    /// let mut window_pose = Pose::new(
2500    ///     [0.01, 0.055, 0.92], Some([0.0, 185.0, 0.0].into()));
2501    ///
2502    /// let sprite = Sprite::from_file("icons/screenshot.png", None, None)
2503    ///                       .expect("open_gltf.jpeg should be ok");
2504    ///
2505    /// filename_scr = "screenshots/ui_layout_push.jpeg";
2506    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2507    ///     Ui::window_begin("Layout Push", &mut window_pose, Some([0.15, 0.13].into()), None, None);
2508    ///     Ui::layout_push([0.1, -0.04, 0.0], [0.01, 0.01], true);
2509    ///     Ui::image(&sprite, [0.07, 0.07]);
2510    ///     Ui::layout_pop();
2511    ///     Ui::layout_push_cut( UiCut::Right, 0.1, true);
2512    ///     Ui::label("Text and more ...", None, false);
2513    ///     Ui::layout_push_cut( UiCut::Bottom, 0.02, true);
2514    ///     Ui::label("And again and again ...", None, false);
2515    ///     Ui::layout_pop();
2516    ///     Ui::layout_pop();
2517    ///     Ui::window_end();
2518    /// );
2519    /// ```
2520    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_layout_push.jpeg" alt="screenshot" width="200">
2521    pub fn layout_push(start: impl Into<Vec3>, dimensions: impl Into<Vec2>, add_margin: bool) {
2522        unsafe { ui_layout_push(start.into(), dimensions.into(), add_margin as Bool32T) };
2523    }
2524
2525    /// This cuts off a portion of the current layout area, and pushes that new area onto the layout stack. Left and Top
2526    /// cuts will always work, but Right and Bottom cuts can only exist inside of a parent layout with an explicit size,
2527    /// auto-resizing prevents these cuts. All UI elements using the layout system will now exist inside this layout
2528    /// area! Note that some UI elements such as Windows will already be managing a layout of their own on the stack.
2529    /// <https://stereokit.net/Pages/StereoKit/UI/LayoutPushCut.html>
2530    /// * `cut_to` - Which side of the current layout should the cut happen to? Note that Right and Bottom will require
2531    ///   explicit sizes in the parent layout, not auto-sizes.   
2532    /// * `size_meters` - The size of the layout cut, in meters.
2533    /// * `add_margin` - Adds a spacing margin to the interior of the layout. Most of the time you won’t need this,
2534    ///   but may be useful when working without a Window.
2535    ///
2536    /// see also [`ui_layout_push_cut`]
2537    /// see example in [`Ui::layout_push`]
2538    pub fn layout_push_cut(cut_to: UiCut, size_meters: f32, add_margin: bool) {
2539        unsafe { ui_layout_push_cut(cut_to, size_meters, add_margin as Bool32T) };
2540    }
2541
2542    /// This removes a layout from the layout stack that was previously added using Ui::layout_push, or
2543    /// Ui::layout_push_cut.
2544    /// <https://stereokit.net/Pages/StereoKit/UI/LayoutPop.html>
2545    ///
2546    /// see also [`ui_layout_pop`]
2547    /// see example in [`Ui::layout_push`]
2548    pub fn layout_pop() {
2549        unsafe { ui_layout_pop() };
2550    }
2551
2552    /// Reserves a box of space for an item in the current UI layout! If either size axis is zero, it will be auto-sized
2553    /// to fill the current surface horizontally, and fill a single line_height vertically. Returns the Hierarchy local
2554    /// bounds of the space that was reserved, with a Z axis dimension of 0.
2555    /// <https://stereokit.net/Pages/StereoKit/UI/LayoutReserve.html>
2556    /// * `size` - Size of the layout box in Hierarchy local meters.
2557    /// * `add_padding` - If true, this will add the current padding value to the total final dimensions of the space that
2558    ///   is reserved.
2559    /// * `depth` - This allows you to quickly insert a depth into the Bounds you’re receiving. This will offset on the
2560    ///   Z axis in addition to increasing the dimensions, so that the bounds still remain sitting on the surface of the
2561    ///   UI. This depth value will not be reflected in the bounds provided by LayouLast.
2562    ///
2563    /// Returns the Hierarchy local bounds of the space that was reserved, with a Z axis dimension of 0.
2564    /// see also [`ui_layout_reserve`]
2565    /// ### Examples
2566    /// ```
2567    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2568    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose, Bounds}};
2569    ///
2570    /// let mut window_pose = Pose::new(
2571    ///     [0.01, 0.055, 0.92], Some([0.0, 185.0, 0.0].into()));
2572    ///
2573    /// test_steps!( // !!!! Get a proper main loop !!!!
2574    ///     Ui::window_begin("Layout Reserve", &mut window_pose, Some([0.2, 0.2].into()), None, None);
2575    ///
2576    ///     let bounds = Ui::layout_reserve([0.05, 0.05], true, 0.005);
2577    ///
2578    ///     let bounds_no_pad = Ui::layout_reserve([0.05, 0.05], false, 0.005);
2579    ///
2580    ///     assert_eq!(bounds, Bounds::new([0.055, -0.045, -0.0025], [0.07, 0.07, 0.005]));
2581    ///     assert_eq!(bounds_no_pad, Bounds::new([0.065, -0.115, -0.0025], [0.05, 0.05, 0.005]));
2582    ///     Ui::window_end();
2583    /// );
2584    /// ```
2585    pub fn layout_reserve(size: impl Into<Vec2>, add_padding: bool, depth: f32) -> Bounds {
2586        unsafe { ui_layout_reserve(size.into(), add_padding as Bool32T, depth) }
2587    }
2588
2589    /// This adds a non-interactive Model to the UI panel layout, and allows you to specify its size.
2590    /// <https://stereokit.net/Pages/StereoKit/UI/Model.html>
2591    /// * `model` - The model to use
2592    /// * `ui_size` - The size this element should take from the layout.
2593    /// * `model_scale` - 0 will auto-scale the model to fit the layout space, but you can specify a different scale in
2594    ///   case you’d like a different size. None will auto-scale the model.
2595    ///
2596    /// see also [`ui_model`]
2597    /// ### Examples
2598    /// ```
2599    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2600    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, model::Model,
2601    ///                      mesh::Mesh, material::Material};
2602    ///
2603    /// let mut window_pose = Pose::new(
2604    ///     [0.01, 0.055, 0.92], Some([0.0, 215.0, 0.0].into()));
2605    ///
2606    /// let model = Model::from_mesh(Mesh::sphere(), Material::pbr());
2607    ///
2608    /// filename_scr = "screenshots/ui_model.jpeg";
2609    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2610    ///     Ui::window_begin("Model", &mut window_pose, None, None, None);
2611    ///     Ui::model(&model, Some([0.03, 0.03].into()), None);
2612    ///     Ui::model(&model, Some([0.04, 0.04].into()), Some(0.05));
2613    ///     Ui::window_end();
2614    /// );
2615    /// ```
2616    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_model.jpeg" alt="screenshot" width="200">
2617    pub fn model(model: impl AsRef<Model>, ui_size: Option<Vec2>, model_scale: Option<f32>) {
2618        let ui_size = ui_size.unwrap_or(Vec2::ZERO);
2619        let model_scale = model_scale.unwrap_or(0.0);
2620        unsafe { ui_model(model.as_ref().0.as_ptr(), ui_size, model_scale) };
2621    }
2622
2623    /// This will advance the layout to the next line. If there’s nothing on the current line, it’ll advance to the
2624    /// start of the next on. But this won’t have any affect on an empty line, try Ui::hspace for that.
2625    /// <https://stereokit.net/Pages/StereoKit/UI/NextLine.html>
2626    ///
2627    /// see also [`ui_nextline`]
2628    /// ### Examples
2629    /// ```
2630    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2631    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
2632    ///
2633    /// let mut window_pose = Pose::new(
2634    ///     [0.01, 0.035, 0.93], Some([0.0, 185.0, 0.0].into()));
2635    ///
2636    /// test_steps!( // !!!! Get a proper main loop !!!!
2637    ///     Ui::window_begin("Next line", &mut window_pose, None, None, None);
2638    ///     Ui::label("Line 1", None, false);
2639    ///     Ui::next_line();
2640    ///     Ui::next_line();
2641    ///     Ui::next_line();
2642    ///     Ui::label("Line 5", None, false);
2643    ///     Ui::window_end();
2644    /// );
2645    /// ```
2646    pub fn next_line() {
2647        unsafe { ui_nextline() };
2648    }
2649
2650    /// If you wish to manually draw a Panel, this function will let you draw one wherever you want!
2651    /// <https://stereokit.net/Pages/StereoKit/UI/PanelAt.html>
2652    /// * `start` - The top left corner of the Panel element.
2653    /// * `size` - The size of the Panel element, in hierarchy local meters.
2654    /// * `padding` - Only UiPad::Outsize has any effect here. UiPad::Inside will behave the same as UiPad::None.
2655    ///
2656    /// see also [`ui_panel_at`] [`Ui::panel_begin`]
2657    /// ### Examples
2658    /// ```
2659    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2660    /// use stereokit_rust::{ui::{Ui, UiPad, UiCut}, maths::{Vec2, Vec3, Pose, Bounds}};
2661    ///
2662    /// let mut window_pose = Pose::new(
2663    ///     [0.01, 0.055, 0.90], Some([0.0, 185.0, 0.0].into()));
2664    ///
2665    /// filename_scr = "screenshots/ui_panel_at.jpeg";
2666    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2667    ///     Ui::window_begin("Panel at", &mut window_pose, Some([0.2, 0.15].into()), None, None);
2668    ///     Ui::panel_at([0.11, -0.01, 0.0], [0.08, 0.03], Some(UiPad::None));
2669    ///     Ui::label("panel 1", None, false);
2670    ///
2671    ///     Ui::layout_push_cut( UiCut::Right, 0.1, true);
2672    ///     Ui::panel_at(Ui::get_layout_at(), Ui::get_layout_remaining(), None);
2673    ///     Ui::label("panel 2", None, false);
2674    ///     Ui::layout_pop();
2675    ///
2676    ///     Ui::layout_push_cut( UiCut::Bottom, 0.08, false);
2677    ///     Ui::panel_at(Ui::get_layout_at(), Ui::get_layout_remaining(), None);
2678    ///     Ui::label("panel 3", None, false);
2679    ///     Ui::layout_pop();
2680    ///
2681    ///     Ui::window_end();
2682    /// );
2683    /// ```
2684    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_panel_at.jpeg" alt="screenshot" width="200">
2685    pub fn panel_at(start: impl Into<Vec3>, size: impl Into<Vec2>, padding: Option<UiPad>) {
2686        let padding = padding.unwrap_or(UiPad::Outside);
2687        unsafe { ui_panel_at(start.into(), size.into(), padding) };
2688    }
2689
2690    /// This will begin a Panel element that will encompass all elements drawn between panel_begin and panel_end. This
2691    /// is an entirely visual element, and is great for visually grouping elements together. Every Begin must have a
2692    /// matching End.
2693    /// <https://stereokit.net/Pages/StereoKit/UI/PanelBegin.html>
2694    /// * `padding` - Describes how padding is applied to the visual element of the Panel. If None the default value is
2695    ///   UiPad::Outside
2696    ///
2697    /// see also [`ui_panel_begin`] [`Ui::panel_at`]
2698    /// ### Examples
2699    /// ```
2700    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2701    /// use stereokit_rust::{ui::{Ui, UiPad, UiCut}, maths::{Vec2, Vec3, Pose}};
2702    ///
2703    /// let mut window_pose = Pose::new(
2704    ///     [0.01, 0.055, 0.90], Some([0.0, 185.0, 0.0].into()));
2705    ///
2706    /// filename_scr = "screenshots/ui_panel_begin.jpeg";
2707    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2708    ///     Ui::window_begin("Panel begin", &mut window_pose, Some([0.2, 0.15].into()), None, None);
2709    ///     Ui::panel_begin(Some(UiPad::None));
2710    ///     Ui::label("panel 1", None, false);
2711    ///     Ui::panel_end();
2712    ///
2713    ///     Ui::layout_push_cut( UiCut::Right, 0.1, true);
2714    ///     Ui::panel_begin(None);
2715    ///     Ui::label("panel 2", None, false);
2716    ///     Ui::panel_end();
2717    ///     Ui::layout_pop();
2718    ///
2719    ///     Ui::layout_push_cut( UiCut::Bottom, 0.08, false);
2720    ///     Ui::panel_begin(Some(UiPad::Inside));
2721    ///     Ui::label("panel 3\nwith CRLF", None, false);
2722    ///     Ui::panel_end();
2723    ///     Ui::layout_pop();
2724    ///
2725    ///     Ui::window_end();
2726    /// );
2727    /// ```
2728    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_panel_begin.jpeg" alt="screenshot" width="200">
2729    pub fn panel_begin(padding: Option<UiPad>) {
2730        let padding = padding.unwrap_or(UiPad::Outside);
2731        unsafe { ui_panel_begin(padding) };
2732    }
2733
2734    /// This will finalize and draw a Panel element.
2735    /// <https://stereokit.net/Pages/StereoKit/UI/PanelEnd.html>
2736    ///
2737    /// see also [`ui_panel_end`]
2738    /// see example in [`Ui::panel_begin`]
2739    pub fn panel_end() {
2740        unsafe { ui_panel_end() };
2741    }
2742
2743    /// This will play the 'on' sound associated with the given UIVisual at the local position. It will also play the
2744    /// 'off' sound when the given element becomes inactive, at the world location of the initial local position!
2745    /// <https://stereokit.net/Pages/StereoKit/UI/PlaySoundOnOff.html>
2746    /// * `element_visual` - The UIVisual to pull sound information from.
2747    /// * `element_id` - The id of the element that will be tracked for playing the 'off' sound.
2748    /// * `at_local` - The hierarchy local location where the sound will play.
2749    ///
2750    /// see also [`ui_play_sound_on_off`] [`Ui::play_sound_on`] [`Ui::play_sound_off`]
2751    /// ### Examples
2752    /// ```
2753    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2754    /// use stereokit_rust::{ui::{Ui, UiVisual}, maths::{Vec3, Pose}};
2755    ///
2756    /// let mut window_pose = Pose::new(
2757    ///     [0.01, 0.035, 0.93], Some([0.0, 185.0, 0.0].into()));
2758    ///
2759    /// let element_visual = UiVisual::WindowBody;
2760    /// let id= "Play sound on/off";
2761    /// let id_hash = Ui::stack_hash(&id);
2762    ///
2763    /// test_steps!( // !!!! Get a proper main loop !!!!
2764    ///     Ui::window_begin(id, &mut window_pose, None, None, None);
2765    ///     Ui::play_sound_on_off(element_visual, id_hash, window_pose.position);
2766    ///     Ui::label("This will play the 'on' sound\nfor the given (id / UiVisual)\nat the local position.", None, false);           
2767    ///     Ui::window_end();
2768    /// );
2769    /// ```
2770    pub fn play_sound_on_off(element_visual: UiVisual, element_id: IdHashT, at_local: impl Into<Vec3>) {
2771        unsafe {
2772            ui_play_sound_on_off(element_visual, element_id, at_local.into());
2773        }
2774    }
2775
2776    /// This will play the 'on' sound associated with the given UIVisual at the world position.
2777    /// <https://stereokit.net/Pages/StereoKit/UI/PlaySoundOn.html>
2778    /// * `element_visual` - The UIVisual to pull sound information from.
2779    /// * `at_local` - The hierarchy local location where the sound will play.
2780    ///
2781    /// see also [`ui_play_sound_on`] [`Ui::play_sound_on_off`] [`Ui::play_sound_off`]
2782    /// ### Examples
2783    /// ```
2784    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2785    /// use stereokit_rust::{ui::{Ui, UiVisual}, maths::{Vec3, Pose}};
2786    ///     
2787    /// let mut window_pose = Pose::new(
2788    ///     [0.01, 0.035, 0.93], Some([0.0, 185.0, 0.0].into()));
2789    ///
2790    /// let element_visual = UiVisual::WindowBody;
2791    ///
2792    /// test_steps!( // !!!! Get a proper main loop !!!!
2793    ///     Ui::window_begin("Play sound on", &mut window_pose, None, None, None);
2794    ///     Ui::play_sound_on(element_visual, window_pose.position);
2795    ///     Ui::label("This will play the 'on' sound\nassociated with the given UIVisual\nat the local position.", None, false);           
2796    ///     Ui::window_end();
2797    /// );
2798    /// ```
2799    pub fn play_sound_on(element_visual: UiVisual, at_local: impl Into<Vec3>) {
2800        unsafe {
2801            ui_play_sound_on(element_visual, at_local.into());
2802        }
2803    }
2804
2805    /// This will play the 'off' sound associated with the given UIVisual at the world position.
2806    /// <https://stereokit.net/Pages/StereoKit/UI/PlaySoundOff.html>
2807    /// * `element_visual` - The UIVisual to pull sound information from.
2808    /// * `at_local` - The hierarchy local location where the sound will play.
2809    ///
2810    /// see also [`ui_play_sound_off`] [`Ui::play_sound_on`] [`Ui::play_sound_on_off`]
2811    /// ### Examples
2812    /// ```
2813    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2814    /// use stereokit_rust::{ui::{Ui, UiVisual}, maths::{Vec3, Pose}};
2815    ///
2816    /// let mut window_pose = Pose::new(
2817    ///     [0.01, 0.035, 0.93], Some([0.0, 185.0, 0.0].into()));
2818    /// let element_visual = UiVisual::SliderPinch;
2819    /// let mut value = 0.5;
2820    ///
2821    /// test_steps!( // !!!! Get a proper main loop !!!!
2822    ///     Ui::window_begin("Play sound off", &mut window_pose, None, None, None);
2823    ///     Ui::hslider("Slider1", &mut value, 0.0, 1.0,  None, None, None, None);
2824    ///     Ui::play_sound_off(element_visual, window_pose.position);
2825    ///     Ui::label("This will play the 'off' sound\nassociated with the given UIVisual\nat the local position.", None, false);
2826    ///     Ui::window_end();
2827    /// );
2828    pub fn play_sound_off(element_visual: UiVisual, at_local: impl Into<Vec3>) {
2829        unsafe {
2830            ui_play_sound_off(element_visual, at_local.into());
2831        }
2832    }
2833
2834    /// Removes an ‘enabled’ state from the stack, and whatever was below will then be used as the primary enabled
2835    /// state.
2836    /// <https://stereokit.net/Pages/StereoKit/UI/PopEnabled.html>
2837    ///
2838    /// see also [`ui_pop_enabled`]
2839    /// see example in [`Ui::push_enabled`]
2840    pub fn pop_enabled() {
2841        unsafe { ui_pop_enabled() };
2842    }
2843
2844    /// Removes the last root id from the stack, and moves up to the one before it!
2845    /// <https://stereokit.net/Pages/StereoKit/UI/PopId.html>
2846    ///
2847    /// see also [`ui_pop_id`]
2848    /// see example in [`Ui::push_id`]
2849    pub fn pop_id() {
2850        unsafe { ui_pop_id() };
2851    }
2852
2853    /// This pops the keyboard presentation state to what it was previously.
2854    /// <https://stereokit.net/Pages/StereoKit/UI/PopPreserveKeyboard.html>
2855    ///
2856    /// see also [`ui_pop_preserve_keyboard`]
2857    /// see example in [`Ui::push_preserve_keyboard`]
2858    pub fn pop_preserve_keyboard() {
2859        unsafe { ui_pop_preserve_keyboard() };
2860    }
2861
2862    /// This removes an enabled status for grab auras from the stack, returning it to the state before the previous
2863    /// push_grab_aura call. Grab auras are an extra space and visual element that goes around Window elements to make
2864    /// them easier to grab.
2865    /// <https://stereokit.net/Pages/StereoKit/UI/PopGrabAurahtml>
2866    ///
2867    /// see also [`ui_pop_grab_aura`]
2868    /// see example in [`Ui::push_grab_aura`]
2869    pub fn pop_grab_aura() {
2870        unsafe { ui_pop_grab_aura() };
2871    }
2872
2873    /// This retreives the top of the grab aura enablement stack, in case you need to know if the current window will
2874    /// have an aura.
2875    /// <https://stereokit.net/Pages/StereoKit/UI/GrabAuraEnabled>
2876    ///
2877    /// see also [`ui_grab_aura_enabled`]
2878    /// see example in [`Ui::push_grab_aura`]
2879    pub fn grab_aura_enabled() -> bool {
2880        unsafe { ui_grab_aura_enabled() != 0 }
2881    }
2882
2883    /// This will return to the previous UI layout on the stack. This must be called after a PushSurface call.
2884    /// <https://stereokit.net/Pages/StereoKit/UI/PopSurface.html>
2885    ///
2886    /// see also [`ui_pop_surface`]
2887    /// see example in [`Ui::push_surface`]
2888    pub fn pop_surface() {
2889        unsafe { ui_pop_surface() };
2890    }
2891
2892    /// Removes a TextStyle from the stack, and whatever was below will then be used as the GUI’s primary font.
2893    /// <https://stereokit.net/Pages/StereoKit/UI/PopTextStyle.html>
2894    ///
2895    /// see also [`ui_pop_text_style`]
2896    /// see example in [`Ui::push_text_style`]
2897    pub fn pop_text_style() {
2898        unsafe { ui_pop_text_style() };
2899    }
2900
2901    /// Removes a Tint from the stack, and whatever was below will then be used as the primary tint.
2902    /// <https://stereokit.net/Pages/StereoKit/UI/PopTint.html>
2903    ///
2904    /// see also [`ui_pop_tint`]
2905    /// see example in [`Ui::push_tint`]
2906    pub fn pop_tint() {
2907        unsafe { ui_pop_tint() };
2908    }
2909
2910    /// This creates a Pose that is friendly towards UI popup windows, or windows that are created due to some type of
2911    /// user interaction. The fallback file picker and soft keyboard both use this function to position themselves!
2912    /// <https://stereokit.net/Pages/StereoKit/UI/PopupPose.html>
2913    /// * `shift` - A positional shift from the default location, this is useful to account for the height of the window,
2914    ///   and center or offset this pose. A value of [0.0, -0.1, 0.0] may be a good starting point.
2915    ///
2916    /// Returns a pose between the UI or hand that is currently active, and the user’s head. Good for popup windows.
2917    /// see also [`ui_popup_pose`]
2918    pub fn popup_pose(shift: impl Into<Vec3>) -> Pose {
2919        unsafe { ui_popup_pose(shift.into()) }
2920    }
2921
2922    /// This is a simple horizontal progress indicator bar. This is used by the hslider to draw the slider bar beneath
2923    /// the interactive element. Does not include any text or label.
2924    /// <https://stereokit.net/Pages/StereoKit/UI/ProgressBar.html>
2925    ///
2926    /// see also [`ui_hprogress_bar`]
2927    #[deprecated(since = "0.0.1", note = "Use HProgressBar instead")]
2928    pub fn progress_bar(percent: f32, width: f32) {
2929        unsafe { ui_hprogress_bar(percent, width, 0) }
2930    }
2931
2932    /// This is a simple horizontal progress indicator bar. This is used by the hslider to draw the slider bar beneath
2933    /// the interactive element. Does not include any text or label.
2934    /// <https://stereokit.net/Pages/StereoKit/UI/HProgressBar.html>
2935    /// * `percent` - A value between 0 and 1 indicating progress from 0% to 100%.
2936    /// * `width` - Physical width of the slider on the window. 0 will fill the remaining amount of window space.
2937    /// * `flip_fill_direction` - By default, this fills from left to right. This allows you to flip the fill direction to
2938    ///   right to left.
2939    ///
2940    /// see also [`ui_hprogress_bar`]
2941    /// see example in [`Ui::progress_bar_at`]
2942    pub fn hprogress_bar(percent: f32, width: f32, flip_fill_direction: bool) {
2943        unsafe { ui_hprogress_bar(percent, width, flip_fill_direction as Bool32T) }
2944    }
2945
2946    /// This is a simple vertical progress indicator bar. This is used by the vslider to draw the slider bar beneath
2947    /// the interactive element. Does not include any text or label.
2948    /// <https://stereokit.net/Pages/StereoKit/UI/VProgressBar.html>
2949    /// * `percent` - A value between 0 and 1 indicating progress from 0% to 100%.
2950    /// * `width` - Physical width of the slider on the window. 0 will fill the remaining amount of window space.
2951    /// * `flip_fill_direction` - By default, this fills from top to bottom. This allows you to flip the fill direction to
2952    ///   bottom to top.
2953    ///
2954    /// see also [`ui_vprogress_bar`]
2955    /// see example in [`Ui::progress_bar_at`]
2956    pub fn vprogress_bar(percent: f32, height: f32, flip_fill_direction: bool) {
2957        unsafe { ui_vprogress_bar(percent, height, flip_fill_direction as Bool32T) }
2958    }
2959
2960    /// This is a simple horizontal progress indicator bar. This is used by the hslider to draw the slider bar beneath
2961    /// the interactive element. Does not include any text or label.
2962    /// <https://stereokit.net/Pages/StereoKit/UI/ProgressBarAt.html>
2963    /// * `percent` - A value between 0 and 1 indicating progress from 0% to 100%.
2964    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
2965    /// * `size` - The layout size for this element in Hierarchy space.
2966    ///
2967    /// see also [`ui_progress_bar_at`] [`Ui::vprogress_bar`] [`Ui::hprogress_bar`]
2968    /// ### Examples
2969    /// ```
2970    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2971    /// use stereokit_rust::{ui::{Ui, UiDir}, maths::{Vec2, Vec3, Pose}, font::Font, system::TextStyle,
2972    ///                      util::named_colors};
2973    ///
2974    /// let mut window_pose = Pose::new(
2975    ///     [0.01, 0.055, 0.92], Some([0.0, 185.0, 0.0].into()));
2976    ///
2977    /// filename_scr = "screenshots/ui_progress_bar_at.jpeg";
2978    /// test_screenshot!( // !!!! Get a proper main loop !!!!
2979    ///     Ui::window_begin("Progress Bar", &mut window_pose, None, None, None);
2980    ///
2981    ///     Ui::vprogress_bar(0.20, 0.08, false);
2982    ///     Ui::progress_bar_at(0.55, [ 0.02,  -0.01, 0.0], [0.01, 0.08], UiDir::Vertical, false);
2983    ///     Ui::progress_bar_at(0.75, [-0.005, -0.01, 0.0], [0.01, 0.08], UiDir::Vertical, false);
2984    ///     Ui::progress_bar_at(0.95, [-0.03,  -0.01, 0.0], [0.01, 0.08], UiDir::Vertical, false);
2985    ///
2986    ///     Ui::hprogress_bar(0.25, 0.1, true);
2987    ///     Ui::progress_bar_at(0.75, [0.05, -0.13, 0.0], [0.1, 0.01], UiDir::Horizontal, true);
2988    ///
2989    ///     Ui::window_end();
2990    /// );
2991    /// ```
2992    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_progress_bar_at.jpeg" alt="screenshot" width="200">
2993    pub fn progress_bar_at(
2994        percent: f32,
2995        top_left_corner: impl Into<Vec3>,
2996        size: impl Into<Vec2>,
2997        bar_direction: UiDir,
2998        flip_fill_direction: bool,
2999    ) {
3000        unsafe {
3001            ui_progress_bar_at(
3002                percent,
3003                top_left_corner.into(),
3004                size.into(),
3005                bar_direction,
3006                flip_fill_direction as Bool32T,
3007            )
3008        }
3009    }
3010
3011    /// All UI between push_enabled and its matching pop_enabled will set the UI to an enabled or disabled state,
3012    /// allowing or preventing interaction with specific elements. The default state is true.
3013    /// <https://stereokit.net/Pages/StereoKit/UI/PushEnabled.html>
3014    /// * `enabled` - Should the following elements be enabled and interactable?
3015    /// * `parent_behavior` - Do we want to ignore or inherit the state of the current stack?
3016    ///   if None, has default value HierarchyParent::Inherit
3017    ///
3018    /// see also [`ui_push_enabled`]
3019    /// ### Examples
3020    /// ```
3021    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3022    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, system::HierarchyParent};
3023    ///
3024    /// let mut window_pose = Pose::new(
3025    ///     [0.01, 0.075, 0.9], Some([0.0, 185.0, 0.0].into()));
3026    /// let mut enabled_value = false;
3027    /// let mut toggle_value = false;
3028    ///
3029    /// test_steps!( // !!!! Get a proper main loop !!!!
3030    ///     Ui::window_begin("Push Enabled", &mut window_pose, None, None, None);
3031    ///     assert_eq!(Ui::get_enabled(), true);
3032    ///     Ui::toggle("Enabled", &mut enabled_value, None);
3033    ///     Ui::push_enabled(enabled_value, None);
3034    ///
3035    ///     Ui::push_enabled(true, None);
3036    ///     assert_eq!(Ui::get_enabled(), false);
3037    ///     Ui::hprogress_bar(0.20, 0.08, false);
3038    ///     Ui::pop_enabled();
3039    ///
3040    ///     let bt2 = Ui::button("Button", None);
3041    ///
3042    ///     Ui::push_enabled(true, Some(HierarchyParent::Ignore));
3043    ///     assert_eq!(Ui::get_enabled(), true);
3044    ///     Ui::toggle("I'm a robot!",&mut toggle_value, None);
3045    ///     Ui::pop_enabled();
3046    ///
3047    ///     Ui::pop_enabled();
3048    ///     Ui::window_end();
3049    /// );
3050    /// ```
3051    pub fn push_enabled(enabled: bool, parent_behavior: Option<HierarchyParent>) {
3052        let parent_behavior = parent_behavior.unwrap_or(HierarchyParent::Inherit);
3053        unsafe { ui_push_enabled(enabled as Bool32T, parent_behavior) }
3054    }
3055
3056    /// Adds a root id to the stack for the following UI elements! This id is combined when hashing any following ids,
3057    /// to prevent id collisions in separate groups.
3058    /// <https://stereokit.net/Pages/StereoKit/UI/PushId.html>
3059    /// * `root_id` - The root id to use until the following PopId call. MUST be unique within current hierarchy.
3060    ///
3061    /// see also [`ui_push_id`]
3062    /// ### Examples
3063    /// ```
3064    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3065    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
3066    ///
3067    /// let mut window_pose = Pose::new(
3068    ///     [0.01, 0.055, 0.92], Some([0.0, 185.0, 0.0].into()));
3069    /// let mut toggle_value1 = false;
3070    /// let mut toggle_value1b = true;
3071    ///
3072    /// test_steps!( // !!!! Get a proper main loop !!!!
3073    ///     Ui::window_begin("Push Id", &mut window_pose, None, None, None);
3074    ///     Ui::push_id("group1");
3075    ///     Ui::toggle("Choice 1",&mut toggle_value1, None);
3076    ///     Ui::pop_id();
3077    ///     Ui::push_id("group2");
3078    ///     Ui::toggle("Choice 1",&mut toggle_value1b, None);
3079    ///     Ui::pop_id();
3080    ///     Ui::window_end();
3081    /// );
3082    /// ```
3083    pub fn push_id(root_id: impl AsRef<str>) -> IdHashT {
3084        let cstr = CString::new(root_id.as_ref()).unwrap();
3085        unsafe { ui_push_id(cstr.as_ptr()) }
3086    }
3087
3088    /// Adds a root id to the stack for the following UI elements! This id is combined when hashing any following ids,
3089    /// to prevent id collisions in separate groups.
3090    /// <https://stereokit.net/Pages/StereoKit/UI/PushId.html>
3091    /// * `root_id` - The root id to use until the following PopId call. MUST be unique within current hierarchy.
3092    ///
3093    /// see also [`ui_push_idi`]
3094    /// ### Examples
3095    /// ```
3096    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3097    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
3098    ///
3099    /// let mut window_pose = Pose::new(
3100    ///     [0.01, 0.055, 0.92], Some([0.0, 185.0, 0.0].into()));
3101    /// let mut toggle_value1 = false;
3102    /// let mut toggle_value1b = true;
3103    ///
3104    /// test_steps!( // !!!! Get a proper main loop !!!!
3105    ///     Ui::window_begin("Push Id", &mut window_pose, None, None, None);
3106    ///     Ui::push_id_int(1);
3107    ///     Ui::toggle("Choice 1",&mut toggle_value1, None);
3108    ///     Ui::pop_id();
3109    ///     Ui::push_id_int(2);
3110    ///     Ui::toggle("Choice 1",&mut toggle_value1b, None);
3111    ///     Ui::pop_id();
3112    ///     Ui::window_end();
3113    /// );
3114    /// ```
3115    pub fn push_id_int(root_id: i32) -> IdHashT {
3116        unsafe { ui_push_idi(root_id) }
3117    }
3118
3119    /// When a soft keyboard is visible, interacting with UI elements will cause the keyboard to close. This function
3120    /// allows you to change this behavior for certain UI elements, allowing the user to interact and still preserve the
3121    /// keyboard’s presence. Remember to Pop when you’re finished!
3122    /// <https://stereokit.net/Pages/StereoKit/UI/PushPreserveKeyboard.html>
3123    /// * `preserve_keyboard` - If true, interacting with elements will NOT hide the keyboard. If false, interaction
3124    ///   will hide the keyboard.
3125    ///
3126    /// see also [`ui_push_preserve_keyboard`]
3127    /// ### Examples
3128    /// ```
3129    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3130    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
3131    ///
3132    /// let mut window_pose = Pose::new(
3133    ///     [0.01, 0.075, 0.9], Some([0.0, 185.0, 0.0].into()));
3134    /// let mut title = String::from("title");
3135    /// let mut author = String::from("author");
3136    /// let mut volume = 0.5;
3137    /// let mut mute = false;
3138    ///
3139    /// test_steps!( // !!!! Get a proper main loop !!!!
3140    ///     Ui::window_begin("Sound track", &mut window_pose, None, None, None);
3141    ///     Ui::push_preserve_keyboard(true);
3142    ///     Ui::input("Title", &mut title, Some([0.15, 0.03].into()), None);
3143    ///     Ui::input("Author", &mut author, Some([0.15, 0.03].into()), None);
3144    ///     Ui::hslider("volume", &mut volume, 0.0, 1.0, Some(0.05), None, None, None);
3145    ///     Ui::toggle("mute", &mut mute, None);
3146    ///     Ui::pop_preserve_keyboard();
3147    ///     Ui::window_end();
3148    /// );
3149    /// ```
3150    pub fn push_preserve_keyboard(preserve_keyboard: bool) {
3151        unsafe { ui_push_preserve_keyboard(preserve_keyboard as Bool32T) }
3152    }
3153
3154    /// This pushes an enabled status for grab auras onto the stack. Grab auras are an extra space and visual element
3155    /// that goes around Window elements to make them easier to grab. MUST be matched by a pop_grab_aura call.
3156    /// <https://stereokit.net/Pages/StereoKit/UI/PushGrabAura.html>
3157    /// * `enabled` - Is the grab aura enabled or not?
3158    ///
3159    /// see also [`ui_push_grab_aura`]
3160    /// ### Examples
3161    /// ```
3162    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3163    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
3164    ///
3165    /// let mut window_pose = Pose::new(
3166    ///     [0.01, 0.075, 0.9], Some([0.0, 185.0, 0.0].into()));
3167    /// let mut title = String::from("");
3168    ///
3169    /// assert_eq!(Ui::grab_aura_enabled(), true);
3170    ///
3171    /// test_steps!( // !!!! Get a proper main loop !!!!
3172    ///     Ui::push_grab_aura(false);
3173    ///     assert_eq!(Ui::grab_aura_enabled(), false);
3174    ///     Ui::window_begin("Write a title", &mut window_pose, None, None, None);
3175    ///     Ui::label("Title:", None, false);
3176    ///     Ui::input("Title", &mut title, Some([0.15, 0.03].into()), None);
3177    ///     Ui::window_end();
3178    ///     Ui::pop_grab_aura();
3179    ///     assert_eq!(Ui::grab_aura_enabled(), true);
3180    /// );
3181    /// ```
3182    pub fn push_grab_aura(enabled: bool) {
3183        unsafe { ui_push_grab_aura(enabled as Bool32T) }
3184    }
3185
3186    /// This will push a surface into SK’s UI layout system. The surface becomes part of the transform hierarchy, and SK
3187    /// creates a layout surface for UI content to be placed on and interacted with. Must be accompanied by a
3188    /// pop_surface call.
3189    /// <https://stereokit.net/Pages/StereoKit/UI/PushSurface.html>
3190    /// * `pose` - The Pose of the UI surface, where the surface forward direction is the same as the Pose’s.
3191    /// * `layout_start` - This is an offset from the center of the coordinate space created by the surfacePose.
3192    ///   Vec3.Zero would mean that content starts at the center of the surfacePose.
3193    /// * `layout_dimension` - The size of the surface area to use during layout. Like other UI layout sizes, an axis
3194    ///   set to zero means it will auto-expand in that direction.
3195    ///
3196    /// see also [`ui_push_surface`]
3197    /// ### Examples
3198    /// ```
3199    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3200    /// use stereokit_rust::{ui::{Ui, UiPad}, maths::{Vec2, Vec3, Pose}, util::named_colors,
3201    ///                      font::Font, system::TextStyle};
3202    /// let mut title = String::from("");
3203    /// let style = TextStyle::from_font(Font::default(), 0.05, named_colors::BLUE);
3204    ///
3205    /// let mut surface_pose = Pose::new(
3206    ///     [-0.09, 0.075, 0.92], Some([0.0, 185.0, 0.0].into()));
3207    ///
3208    /// filename_scr = "screenshots/ui_push_surface.jpeg";
3209    /// test_screenshot!( // !!!! Get a proper main loop !!!!
3210    ///     Ui::push_surface(surface_pose, [0.0, 0.0, 0.0], [0.1, 0.1]);
3211    ///     Ui::push_text_style(style);
3212    ///     Ui::label("Surface", Some([0.25, 0.03].into()), false);
3213    ///     Ui::pop_text_style();
3214    ///     Ui::panel_begin(Some(UiPad::Inside));
3215    ///     Ui::label("Give a title:", None, false);
3216    ///     Ui::input("Title", &mut title, Some([0.15, 0.03].into()), None);
3217    ///     Ui::panel_end();
3218    ///     Ui::pop_surface();
3219    /// );
3220    /// ```
3221    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_push_surface.jpeg" alt="screenshot" width="200">
3222    pub fn push_surface(pose: impl Into<Pose>, layout_start: impl Into<Vec3>, layout_dimension: impl Into<Vec2>) {
3223        unsafe { ui_push_surface(pose.into(), layout_start.into(), layout_dimension.into()) }
3224    }
3225
3226    /// This pushes a Text Style onto the style stack! All text elements rendered by the GUI system will now use this
3227    /// styling.
3228    /// <https://stereokit.net/Pages/StereoKit/UI/PushTextStyle.html>
3229    ///
3230    /// see also [`ui_push_text_style`]
3231    /// ### Examples
3232    /// ```
3233    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3234    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, font::Font, system::TextStyle,
3235    ///                      util::named_colors};
3236    ///
3237    /// let mut window_pose = Pose::new(
3238    ///     [0.01, 0.035, 0.92], Some([0.0, 185.0, 0.0].into()));
3239    ///
3240    /// let font = Font::default();
3241    /// let style_big    = TextStyle::from_font(&font, 0.015, named_colors::CYAN);
3242    /// let style_medium = TextStyle::from_font(&font, 0.012, named_colors::FUCHSIA);
3243    /// let style_small  = TextStyle::from_font(&font, 0.009, named_colors::GOLD);
3244    /// let style_mini   = TextStyle::from_font(&font, 0.006, named_colors::WHITE);
3245    ///
3246    /// filename_scr = "screenshots/ui_push_text_style.jpeg";
3247    /// test_screenshot!( // !!!! Get a proper main loop !!!!
3248    ///     Ui::window_begin("Push TextStyle", &mut window_pose, Some([0.16, 0.0].into()), None, None);
3249    ///
3250    ///     Ui::push_text_style(style_big);
3251    ///     Ui::text("The first part", None, None, None, None, None, None);
3252    ///     assert_eq!(Ui::get_text_style(), style_big);
3253    ///     Ui::pop_text_style();
3254    ///
3255    ///     Ui::push_text_style(style_medium);
3256    ///     Ui::text("The second part", None, None, None, None, None, None);
3257    ///     Ui::pop_text_style();
3258    ///
3259    ///     Ui::push_text_style(style_small);
3260    ///     Ui::text("The third part", None, None, None, None, None, None);
3261    ///     Ui::push_text_style(style_mini);
3262    ///     Ui::text("The Inside part", None, None, None, None, None, None);
3263    ///     assert_eq!(Ui::get_text_style(), style_mini);
3264    ///     Ui::pop_text_style();
3265    ///     Ui::text("----////", None, None, None, None, None, None);
3266    ///     Ui::pop_text_style();
3267    ///
3268    ///     Ui::window_end();
3269    /// );
3270    /// ```
3271    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_push_text_style.jpeg" alt="screenshot" width="200">
3272    pub fn push_text_style(style: TextStyle) {
3273        unsafe { ui_push_text_style(style) }
3274    }
3275
3276    /// All UI between push_tint and its matching pop_tint will be tinted with this color. This is implemented by
3277    /// multiplying this color with the current color of the UI element. The default is a White (1,1,1,1) identity tint.
3278    /// <https://stereokit.net/Pages/StereoKit/UI/PushTint.html>
3279    /// * `color_gamma` - A normal (gamma corrected) color value. This is internally converted to linear, so tint
3280    ///   multiplication happens on linear color values.
3281    ///
3282    /// see also [`ui_push_tint`] [`Ui::color_scheme`]
3283    /// ### Examples
3284    /// ```
3285    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3286    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}, util::Color128};
3287    /// let mut title = String::from("Push Tint");
3288    /// let mut volume = 0.5;
3289    /// let mut mute = true;
3290    ///
3291    /// let mut window_pose = Pose::new(
3292    ///     [0.01, 0.05, 0.92], Some([0.0, 185.0, 0.0].into()));
3293    ///
3294    /// let blue  = Color128::rgb(0.0, 0.0, 1.0);
3295    /// let red   = Color128::rgb(1.0, 0.0, 0.0);
3296    /// let green = Color128::rgb(0.0, 1.0, 0.0);
3297    ///
3298    /// filename_scr = "screenshots/ui_push_tint.jpeg";
3299    /// test_screenshot!( // !!!! Get a proper main loop !!!!
3300    ///     Ui::window_begin("Push Tint", &mut window_pose, None, None, None);
3301    ///     Ui::push_tint(blue.to_gamma());
3302    ///     Ui::input("Title", &mut title, Some([0.15, 0.03].into()), None);
3303    ///     Ui::pop_tint();
3304    ///     Ui::push_tint(red.to_gamma());
3305    ///     Ui::hslider("volume", &mut volume, 0.0, 1.0, Some(0.05), None, None, None);
3306    ///     Ui::pop_tint();
3307    ///     Ui::push_tint(green.to_gamma());
3308    ///     Ui::toggle("mute", &mut mute, None);
3309    ///     Ui::pop_tint();
3310    ///     Ui::window_end();
3311    /// );
3312    /// ```
3313    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_push_tint.jpeg" alt="screenshot" width="200">
3314    pub fn push_tint(color_gamma: impl Into<Color128>) {
3315        unsafe { ui_push_tint(color_gamma.into()) }
3316    }
3317
3318    /// This will reposition the Mesh’s vertices to work well with quadrant resizing shaders. The mesh should generally
3319    /// be centered around the origin, and face down the -Z axis. This will also overwrite any UV coordinates in the
3320    /// verts.
3321    ///
3322    /// You can read more about the technique here : <https://playdeck.net/blog/quadrant-sizing-efficient-ui-rendering>
3323    /// <https://stereokit.net/Pages/StereoKit/UI/QuadrantSizeMesh.html>
3324    /// * `mesh` - The vertices of this Mesh will be retrieved, modified, and overwritten.
3325    /// * `overflow_percent` - When scaled, should the geometry stick out past the “box” represented by the scale, or
3326    ///   edge up against it? A value of 0 will mean the geometry will fit entirely inside the “box”, and a value of 1
3327    ///   means the geometry will start at the boundary of the box and continue outside it.
3328    ///
3329    /// see also [`ui_quadrant_size_mesh`]
3330    /// ### Examples
3331    /// ```
3332    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3333    /// use stereokit_rust::{ui::{Ui, UiCorner, UiVisual, UiLathePt}, maths::{Vec2, Vec3, Pose, Matrix},
3334    ///                      mesh::Mesh, material::Material, util::named_colors};
3335    ///
3336    /// let mut window_pose = Pose::new(
3337    ///     [0.01, 0.025, 0.948], Some([0.0, 185.0, 0.0].into()));
3338    ///
3339    /// let material = Material::pbr();
3340    /// let transform1 = Matrix::t_r_s([-0.1, 0.0, 0.74], [0.0, 130.0, 0.0], [3.0, 1.0, 0.05]);
3341    ///
3342    /// let mut mesh = Mesh::generate_cube([1.0, 1.0, 1.0], None);
3343    /// Ui::quadrant_size_mesh(&mut mesh, 0.20);
3344    ///
3345    /// let bounds = mesh.get_bounds();
3346    /// assert_eq!(bounds.center, Vec3 { x: 0.0, y: 0.0, z: 0.0 });
3347    /// //TODO:
3348    /// assert_eq!(bounds.dimensions, Vec3 { x: 0.0, y: 0.0, z: 1.0 });
3349    ///
3350    /// Ui::set_element_visual(UiVisual::Separator, mesh, None, None);
3351    ///
3352    /// test_steps!( // !!!! Get a proper main loop !!!!
3353    ///     Ui::window_begin("Push Tint", &mut window_pose, None, None, None);
3354    ///     Ui::hseparator();
3355    ///     if Ui::button("Exit", None) {sk.quit(None);}
3356    ///     Ui::hseparator();
3357    ///     Ui::window_end();
3358    /// );
3359    /// ```
3360    pub fn quadrant_size_mesh(mesh: impl AsRef<Mesh>, overflow_percent: f32) {
3361        unsafe { ui_quadrant_size_mesh(mesh.as_ref().0.as_ptr(), overflow_percent) }
3362    }
3363
3364    /// This will reposition the vertices to work well with quadrant resizing shaders. The mesh should generally be
3365    /// centered around the origin, and face down the -Z axis. This will also overwrite any UV coordinates in the verts.
3366    ///
3367    /// You can read more about the technique here : <https://playdeck.net/blog/quadrant-sizing-efficient-ui-rendering>
3368    /// <https://stereokit.net/Pages/StereoKit/UI/QuadrantSizeVerts.html>
3369    /// * `verts` - A list of vertices to be modified to fit the sizing shader.
3370    /// * `overflow_percent` - When scaled, should the geometry stick out past the “box” represented by the scale, or
3371    ///   edge up against it? A value of 0 will mean the geometry will fit entirely inside the “box”, and a value of 1
3372    ///   means the geometry will start at the boundary of the box and continue outside it.
3373    ///
3374    /// see also [`ui_quadrant_size_verts`]
3375    /// ### Examples
3376    /// ```
3377    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3378    /// use stereokit_rust::{ui::{Ui, UiCorner, UiVisual, UiLathePt},
3379    ///                      maths::{Vec2, Vec3, Pose, Matrix},
3380    ///                      mesh::Mesh, material::Material, util::named_colors};
3381    ///
3382    /// let mut window_pose = Pose::new(
3383    ///     [0.01, 0.025, 0.948], Some([0.0, 185.0, 0.0].into()));
3384    ///
3385    /// let material = Material::pbr();
3386    /// let transform1 = Matrix::t_r_s([-0.1, 0.0, 0.74], [0.0, 130.0, 0.0], [3.0, 1.0, 0.05]);
3387    ///
3388    /// let mut mesh = Mesh::generate_cube([1.0, 1.0, 1.0], None);
3389    /// let mut verts = mesh.get_verts();
3390    /// Ui::quadrant_size_verts(&verts, 0.0);
3391    /// let mut remesh = mesh.clone_ref();
3392    /// remesh.set_verts(&verts, true);
3393    ///
3394    /// let bounds = mesh.get_bounds();
3395    /// assert_eq!(bounds.center, Vec3 { x: 0.0, y: 0.0, z: 0.0 });
3396    /// //TODO:
3397    /// assert_eq!(bounds.dimensions, Vec3 { x: 0.0, y: 0.0, z: 1.0 });
3398    ///
3399    /// Ui::set_element_visual(UiVisual::Separator, mesh, None, None);
3400    ///
3401    /// test_steps!( // !!!! Get a proper main loop !!!!
3402    ///     Ui::window_begin("Push Tint", &mut window_pose, None, None, None);
3403    ///     Ui::hseparator();
3404    ///     if Ui::button("Exit", None) {sk.quit(None);}
3405    ///     Ui::hseparator();
3406    ///     Ui::window_end();
3407    /// );
3408    /// ```
3409    pub fn quadrant_size_verts(verts: &[Vertex], overflow_percent: f32) {
3410        unsafe { ui_quadrant_size_verts(verts.as_ptr() as *mut Vertex, verts.len() as i32, overflow_percent) }
3411    }
3412
3413    /// This generates a quadrantified mesh meant for UI buttons by sweeping a lathe over the rounded corners of a
3414    /// rectangle! Note that this mesh is quadrantified, so it requires special shaders to draw properly!
3415    /// <https://stereokit.net/Pages/StereoKit/UI/GenQuadrantMesh.html>
3416    /// * `rounded_corners` - A bit-flag indicating which corners should be rounded, and which should be sharp!
3417    /// * `corner_radius` - The radius of each rounded corner.
3418    /// * `corner_resolution` - How many slices/verts go into each corner? More is smoother, but more expensive to render.
3419    /// * `delete_flat_sides` - If two adjacent corners are sharp, should we skip connecting them with triangles? If this
3420    ///   edge will always be covered, then deleting these faces may save you some performance.
3421    /// * `quadrantify` - Does this generate a mesh compatible with StereoKit's quadrant shader system, or is this just a
3422    ///   traditional mesh? In most cases, this should be true, but UI elements such as the rounded button may be
3423    ///   exceptions.
3424    /// * `lathe_pts`" - The lathe points to sweep around the edge.
3425    ///
3426    /// Returns the final Mesh, ready for use in SK's theming system.
3427    /// see also [`ui_gen_quadrant_mesh`]
3428    /// ### Examples
3429    /// ```
3430    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3431    /// use stereokit_rust::{ui::{Ui, UiCorner, UiVisual, UiLathePt}, maths::{Vec2, Vec3, Pose, Matrix},
3432    ///                      mesh::Mesh, material::Material, util::named_colors};
3433    ///
3434    /// let mut window_pose = Pose::new(
3435    ///     [0.01, 0.028, 0.92], Some([0.0, 185.0, 0.0].into()));
3436    ///
3437    /// let material = Material::pbr();
3438    /// let mut mesh_button = Ui::gen_quadrant_mesh(
3439    ///     UiCorner::All, 0.002, 8, false, true, &UiLathePt::button())
3440    ///                        .expect("mesh should be created");
3441    /// let mut mesh_input = Ui::gen_quadrant_mesh(
3442    ///     UiCorner::All, 0.018, 8, false, true, &UiLathePt::input())
3443    ///                        .expect("mesh should be created");
3444    ///
3445    /// let bounds = mesh_button.get_bounds();
3446    /// assert_eq!(bounds.center, Vec3 { x: 0.0, y: 0.0, z: -0.005 });
3447    /// assert_eq!(bounds.dimensions, Vec3 { x: 0.004, y: 0.004, z: 0.99 });
3448    ///
3449    /// Ui::set_element_visual(UiVisual::Button, mesh_button, None, None);
3450    /// Ui::set_element_visual(UiVisual::Input, mesh_input, None, None);
3451    ///
3452    /// let mut text = String::from("Text");
3453    ///
3454    /// filename_scr = "screenshots/ui_gen_quadrant_mesh.jpeg";
3455    /// test_screenshot!( // !!!! Get a proper main loop !!!!
3456    ///     Ui::window_begin("gen_quadrant_mesh", &mut window_pose, None, None, None);
3457    ///     Ui::input("input", &mut text, None, None );
3458    ///     if Ui::button("Exit", None) {sk.quit(None);}
3459    ///     Ui::window_end();
3460    /// );
3461    /// ```
3462    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_gen_quadrant_mesh.jpeg" alt="screenshot" width="200">
3463    pub fn gen_quadrant_mesh(
3464        rounded_corners: UiCorner,
3465        corner_radius: f32,
3466        corner_resolution: u32,
3467        delete_flat_sides: bool,
3468        quadrantify: bool,
3469        lathe_pts: &[UiLathePt],
3470    ) -> Result<Mesh, StereoKitError> {
3471        match NonNull::new(unsafe {
3472            ui_gen_quadrant_mesh(
3473                rounded_corners,
3474                corner_radius,
3475                corner_resolution,
3476                delete_flat_sides as Bool32T,
3477                quadrantify as Bool32T,
3478                lathe_pts.as_ptr(),
3479                lathe_pts.len() as i32,
3480            )
3481        }) {
3482            Some(mesh_t) => Ok(Mesh(mesh_t)),
3483            None => Err(StereoKitError::MeshGen("gen_quadrant_mesh failed !".to_owned())),
3484        }
3485    }
3486
3487    /// A Radio is similar to a button, except you can specify if it looks pressed or not regardless of interaction.
3488    /// This can be useful for radio-like behavior! Check an enum for a value, and use that as the ‘active’ state, Then
3489    /// switch to that enum value if Radio returns true.
3490    /// <https://stereokit.net/Pages/StereoKit/UI/Radio.html>
3491    /// * `text` - Text to display on the Radio and id for tracking element state. MUST be unique within current
3492    ///   hierarchy.
3493    /// * `active` - Does this button look like it’s pressed?
3494    /// * `size` - The layout size for this element in Hierarchy space. If an axis is left as zero, it will be
3495    ///   auto-calculated. For X this is the remaining width of the current layout, and for Y this is
3496    ///   [`Ui::get_line_height`].
3497    ///
3498    /// Returns true only on the first frame it is pressed.
3499    /// see also [`ui_toggle_img`] [`ui_toggle_img_sz`]
3500    #[deprecated(since = "0.0.1", note = "Performence issues, use radio_img instead")]
3501    pub fn radio(text: impl AsRef<str>, active: bool, size: Option<Vec2>) -> bool {
3502        let cstr = CString::new(text.as_ref()).unwrap();
3503        let mut active: Bool32T = active as Bool32T;
3504        let active_ptr: *mut Bool32T = &mut active;
3505        match size {
3506            Some(size) => unsafe {
3507                ui_toggle_img_sz(
3508                    cstr.as_ptr(),
3509                    active_ptr,
3510                    Sprite::radio_off().0.as_ptr(),
3511                    Sprite::radio_on().0.as_ptr(),
3512                    UiBtnLayout::Left,
3513                    size,
3514                ) != 0
3515            },
3516            None => unsafe {
3517                ui_toggle_img(
3518                    cstr.as_ptr(),
3519                    active_ptr,
3520                    Sprite::radio_off().0.as_ptr(),
3521                    Sprite::radio_on().0.as_ptr(),
3522                    UiBtnLayout::Left,
3523                ) != 0
3524            },
3525        }
3526    }
3527
3528    /// A Radio is similar to a button, except you can specify if it looks pressed or not regardless of interaction.
3529    /// This can be useful for radio-like behavior! Check an enum for a value, and use that as the ‘active’ state, Then
3530    /// switch to that enum value if Radio returns true.
3531    /// This version allows you to override the images used by the Radio.
3532    /// <https://stereokit.net/Pages/StereoKit/UI/Radio.html>
3533    /// * `text` - Text to display on the Radio and id for tracking element state. MUST be unique within current
3534    ///   hierarchy.
3535    /// * `active` - Does this button look like it’s pressed?
3536    /// * `image_off` - Image to use when the radio value is false.
3537    /// * `image_on` - Image to use when the radio value is true.
3538    /// * `image_layout` - This enum specifies how the text and image should be laid out on the radio. For example,
3539    ///   UiBtnLayout::Left will have the image on the left, and text on the right.
3540    /// * `size` - The layout size for this element in Hierarchy space. If an axis is left as zero, it will be
3541    ///   auto-calculated. For X this is the remaining width of the current layout, and for Y this is
3542    ///   [`Ui::get_line_height`].
3543    ///
3544    /// Returns true only on the first frame it is pressed.
3545    /// see also [`ui_toggle_img`] [`ui_toggle_img_sz`] [Ui::radio_at]
3546    /// ### Examples
3547    /// ```
3548    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3549    /// use stereokit_rust::{ui::{Ui, UiBtnLayout}, maths::{Vec2, Vec3, Pose}, sprite::Sprite};
3550    ///
3551    /// let mut window_pose = Pose::new(
3552    ///     [0.01, 0.035, 0.91], Some([0.0, 185.0, 0.0].into()));
3553    ///
3554    /// let (on, off) = (Sprite::radio_on(), Sprite::radio_off());
3555    ///
3556    /// let mut choice = "A";
3557    ///
3558    /// filename_scr = "screenshots/ui_radio.jpeg";
3559    /// test_screenshot!( // !!!! Get a proper main loop !!!!
3560    ///     Ui::window_begin("Radio", &mut window_pose, None, None, None);
3561    ///     if Ui::radio_img("A", choice == "A", &off, &on, UiBtnLayout::Right,
3562    ///                      Some([0.06, 0.05].into())) {
3563    ///         choice = "A";
3564    ///     }
3565    ///     Ui::same_line();
3566    ///     if Ui::radio_img("B", choice == "B", &off, &on, UiBtnLayout::Center,
3567    ///                      Some([0.03, 0.05].into())){
3568    ///         choice = "B";
3569    ///     }
3570    ///     Ui::same_line();
3571    ///     if Ui::radio_img("C", choice == "C", &off, &on, UiBtnLayout::Left, None) {
3572    ///         choice = "C";
3573    ///     }
3574    ///     if Ui::radio_at("D", choice == "D", &off, &on, UiBtnLayout::Right,
3575    ///                     [0.06, -0.07, 0.0], [0.06, 0.03]) {
3576    ///         choice = "D";
3577    ///     }    
3578    ///     if Ui::radio_at("E", choice == "E", &off, &on, UiBtnLayout::Left,
3579    ///                     [-0.01, -0.07, 0.0], [0.06, 0.03]) {
3580    ///         choice = "E";
3581    ///     }
3582    ///     Ui::window_end();
3583    /// );
3584    /// ```
3585    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_radio.jpeg" alt="screenshot" width="200">
3586    pub fn radio_img(
3587        text: impl AsRef<str>,
3588        active: bool,
3589        image_off: impl AsRef<Sprite>,
3590        image_on: impl AsRef<Sprite>,
3591        image_layout: UiBtnLayout,
3592        size: Option<Vec2>,
3593    ) -> bool {
3594        let cstr = CString::new(text.as_ref()).unwrap();
3595        let mut active: Bool32T = active as Bool32T;
3596        let active_ptr: *mut Bool32T = &mut active;
3597        match size {
3598            Some(size) => unsafe {
3599                ui_toggle_img_sz(
3600                    cstr.as_ptr(),
3601                    active_ptr,
3602                    image_off.as_ref().0.as_ptr(),
3603                    image_on.as_ref().0.as_ptr(),
3604                    image_layout,
3605                    size,
3606                ) != 0
3607            },
3608            None => unsafe {
3609                ui_toggle_img(
3610                    cstr.as_ptr(),
3611                    active_ptr,
3612                    image_off.as_ref().0.as_ptr(),
3613                    image_on.as_ref().0.as_ptr(),
3614                    image_layout,
3615                ) != 0
3616            },
3617        }
3618    }
3619
3620    /// A Radio is similar to a button, except you can specify if it looks pressed or not regardless of interaction.
3621    /// This can be useful for radio-like behavior! Check an enum for a value, and use that as the ‘active’ state, Then
3622    /// switch to that enum value if Radio returns true. This version allows you to override the images used by
3623    /// the Radio.
3624    /// <https://stereokit.net/Pages/StereoKit/UI/RadioAt.html>
3625    /// * `text` - Text to display on the Radio and id for tracking element state. MUST be unique within current
3626    ///   hierarchy.
3627    /// * `active` - Does this button look like it’s pressed?
3628    /// * `image_off` - Image to use when the radio value is false.
3629    /// * `image_on` - Image to use when the radio value is true.
3630    /// * `image_layout` - This enum specifies how the text and image should be laid out on the radio. For example,
3631    ///   UiBtnLayout::Left will have the image on the left, and text on the right.
3632    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
3633    /// * size - The layout size for this element in Hierarchy space.
3634    ///
3635    /// Returns true only on the first frame it is pressed.
3636    /// see also [`ui_toggle_img_at`]
3637    /// see example in [`Ui::radio_img`]
3638    pub fn radio_at(
3639        text: impl AsRef<str>,
3640        active: bool,
3641        image_off: impl AsRef<Sprite>,
3642        image_on: impl AsRef<Sprite>,
3643        image_layout: UiBtnLayout,
3644        top_left_corner: impl Into<Vec3>,
3645        size: impl Into<Vec2>,
3646    ) -> bool {
3647        let cstr = CString::new(text.as_ref()).unwrap();
3648        let mut active: Bool32T = active as Bool32T;
3649        let active_ptr: *mut Bool32T = &mut active;
3650        unsafe {
3651            ui_toggle_img_at(
3652                cstr.as_ptr(),
3653                active_ptr,
3654                image_off.as_ref().0.as_ptr(),
3655                image_on.as_ref().0.as_ptr(),
3656                image_layout,
3657                top_left_corner.into(),
3658                size.into(),
3659            ) != 0
3660        }
3661    }
3662
3663    /// Moves the current layout position back to the end of the line that just finished, so it can continue on the same
3664    /// line as the last element!
3665    /// <https://stereokit.net/Pages/StereoKit/UI/SameLine.html>
3666    ///
3667    /// see also [`ui_sameline`]
3668    pub fn same_line() {
3669        unsafe { ui_sameline() }
3670    }
3671
3672    /// Override the visual assets attached to a particular UI element.
3673    /// Note that StereoKit’s default UI assets use a type of quadrant sizing that is implemented in the Material and
3674    /// the Mesh. You don’t need to use quadrant sizing for your own visuals, but if you wish to know more, you can read
3675    /// more about the technique here : <https://playdeck.net/blog/quadrant-sizing-efficient-ui-rendering>
3676    /// You may also find Ui::quadrant_size_verts and Ui::quadrant_size_mesh to be helpful.
3677    /// <https://stereokit.net/Pages/StereoKit/UI/SetElementVisual.html>
3678    /// * `visual` - Which UI visual element to override. Use UiVisual::ExtraSlotXX if you need extra
3679    ///   UIVisual slots for your own custom UI elements.
3680    /// * `mesh` - The Mesh to use for the UI element's visual component. The Mesh will be scaled to match the dimensions
3681    ///   of the UI element.
3682    /// * `material` - The Material to use when rendering the UI element. None is for the default Material specifically
3683    ///   designed to work with quadrant sizing formatted meshes.
3684    /// * `min_size` - For some meshes, such as quadrant sized meshes, there's a minimum size where the mesh turns inside
3685    ///   out. This lets UI elements to accommodate for this minimum size, and behave somewhat more appropriately. None
3686    ///   is Vec2::ZERO.
3687    ///
3688    /// see also [`ui_set_element_visual`]
3689    /// see example in [`Ui::gen_quadrant_mesh`]
3690    /// ### Examples
3691    /// ```
3692    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3693    /// use stereokit_rust::{ui::{Ui, UiColor, UiVisual, UiLathePt, UiCorner}, maths::{Vec2, Vec3, Pose},
3694    ///                      mesh::Mesh, material::Material, util::named_colors};
3695    ///
3696    /// let mut window_pose = Pose::new(
3697    ///     [0.01, 0.025, 0.92], Some([0.0, 185.0, 0.0].into()));
3698    ///
3699    /// let material = Material::pbr();
3700    /// let mut mesh = Ui::gen_quadrant_mesh(
3701    ///     UiCorner::All, 0.005, 8, false, true, &UiLathePt::plane())
3702    ///                        .expect("mesh should be created");
3703    ///
3704    /// Ui::set_element_visual(UiVisual::Separator, mesh, None, None);
3705    ///
3706    /// test_steps!( // !!!! Get a proper main loop !!!!
3707    ///     Ui::window_begin("set element visual", &mut window_pose, None, None, None);
3708    ///     Ui::hseparator();
3709    ///     if Ui::button("Exit", None) {sk.quit(None);}
3710    ///     Ui::hseparator();
3711    ///     Ui::window_end();
3712    /// );
3713    /// ```
3714    pub fn set_element_visual(
3715        visual: UiVisual,
3716        mesh: impl AsRef<Mesh>,
3717        material: Option<Material>,
3718        min_size: Option<Vec2>,
3719    ) {
3720        let material = match material {
3721            Some(mat) => mat.0.as_ptr(),
3722            None => null_mut(),
3723        };
3724        let min_size = min_size.unwrap_or_default();
3725        unsafe { ui_set_element_visual(visual, mesh.as_ref().0.as_ptr(), material, min_size) };
3726    }
3727
3728    /// This allows you to override the color category that a UI element is assigned to.
3729    /// <https://stereokit.net/Pages/StereoKit/UI/SetElementColor.html>
3730    /// * `visual` - The UI element type to set the color category of.
3731    /// * `color_category` - The category of color to assign to this UI element. Use Ui::set_theme_color in combination
3732    ///   with this to assign a specific color. Use UiColor::ExtraSlotXX if you need extra UIColor slots
3733    ///   for your own custom UI elements.
3734    ///
3735    /// see also [`ui_set_element_color`]
3736    /// ### Examples
3737    /// ```
3738    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3739    /// use stereokit_rust::{ui::{Ui, UiColor, UiVisual}, maths::{Vec2, Vec3, Pose},
3740    ///                      util::{named_colors, Color128}};
3741    ///
3742    /// let mut window_pose = Pose::new(
3743    ///     [0.01, 0.025, 0.93], Some([0.0, 185.0, 0.0].into()));
3744    ///
3745    /// assert_eq!(Ui::get_element_color(UiVisual::Separator, 1.0),
3746    ///            Color128 { r: 1.0620984, g: 0.49995762, b: 0.2311526, a: 1.0 });
3747    ///
3748    /// Ui::set_element_color(UiVisual::Separator, UiColor::Complement);
3749    /// assert_eq!(Ui::get_element_color(UiVisual::Separator, 1.0),
3750    ///            Color128 { r: 0.10546647, g: 0.092475444, b: 0.08364652, a: 1.0 });
3751    ///
3752    /// assert_eq!(Ui::get_element_color(UiVisual::Button, 0.0),
3753    ///            Color128 { r: 0.2058468, g: 0.1961254, b: 0.18924558, a: 0.0 });
3754    /// Ui::set_element_color(UiVisual::Button, UiColor::Background);
3755    /// assert_eq!(Ui::get_element_color(UiVisual::Button, 0.0),
3756    ///            Color128 { r: 0.091664724, g: 0.08037374, b: 0.072700225, a: 0.0 });
3757    ///
3758    /// test_steps!( // !!!! Get a proper main loop !!!!
3759    ///     Ui::window_begin("set_element_color", &mut window_pose, None, None, None);
3760    ///     Ui::hseparator();
3761    ///     if Ui::button("Exit", None) {sk.quit(None);}
3762    ///     Ui::hseparator();
3763    ///     Ui::window_end();
3764    /// );
3765    /// ```
3766    pub fn set_element_color(visual: UiVisual, color_category: UiColor) {
3767        unsafe { ui_set_element_color(visual, color_category) };
3768    }
3769
3770    /// This sets the sound that a particulat UI element will make when you interact with it. One sound when the
3771    /// interaction starts, and one when it ends.
3772    /// <https://stereokit.net/Pages/StereoKit/UI/SetElementSound.html>
3773    /// * `visual` - The UI element to apply the sounds to. Use UiVisual::ExtraSlotXX if you need extra
3774    ///   UIVisual slots
3775    /// * `activate` - The sound made when the interaction begins. None will fall back to the default sound.
3776    /// * `deactivate` - The sound made when the interaction ends. None will fall back to the default sound.
3777    ///
3778    /// see also [`ui_set_element_sound`]
3779    /// ### Examples
3780    /// ```
3781    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3782    /// use stereokit_rust::{ui::{Ui, UiVisual}, maths::{Vec2, Vec3, Pose}, sound::Sound};
3783    ///
3784    /// let mut window_pose = Pose::new(
3785    ///     [0.01, 0.075, 0.9], Some([0.0, 185.0, 0.0].into()));
3786    ///
3787    /// let sound_activate = Sound::click();
3788    /// let sound_deactivate = Sound::unclick();
3789    ///
3790    /// Ui::set_element_sound(UiVisual::Button, Some(sound_activate), Some(sound_deactivate));
3791    ///
3792    /// test_steps!( // !!!! Get a proper main loop !!!!
3793    ///     Ui::window_begin("Set Element Sound", &mut window_pose, None, None, None);
3794    ///     if Ui::button("Button1", None) {todo!();}
3795    ///     if Ui::button("Button2", None) {todo!();}
3796    ///     Ui::window_end();
3797    /// );
3798    /// ```
3799    pub fn set_element_sound(visual: UiVisual, activate: Option<Sound>, deactivate: Option<Sound>) {
3800        let activate = match activate {
3801            Some(sound) => sound.0.as_ptr(),
3802            None => null_mut(),
3803        };
3804        let deactivate = match deactivate {
3805            Some(sound) => sound.0.as_ptr(),
3806            None => null_mut(),
3807        };
3808        unsafe { ui_set_element_sound(visual, activate, deactivate) };
3809    }
3810
3811    /// This will draw a visual element from StereoKit's theming system, while paying attention to certain factors
3812    /// such as enabled/disabled, tinting and more.
3813    /// <https://stereokit.net/Pages/StereoKit/UI/DrawElement.html>
3814    /// * `element_visual` - The element type to draw. Use UiVisual::ExtraSlotXX to use extra UiVisual
3815    ///   slots for your own custom UI elements. If these slots are empty, SK will fall back to UiVisual::Default
3816    /// * `element_color` - If you wish to use the coloring from a different element, you can use this to override the
3817    ///   theme color used when drawing. Use UiVisual::ExtraSlotXX to use extra UiVisual slots for your
3818    ///   own custom UI elements. If these slots are empty, SK will fall back to UiVisual::Default.
3819    /// * `start` - This is the top left corner of the UI element relative to the current Hierarchy.
3820    /// * `size` - The layout size for this element in Hierarchy space.
3821    /// * `focus` - The amount of visual focus this element currently has, where 0 is unfocused, and 1 is active. You
3822    ///   can acquire a good focus value from `Ui::get_anim_focus`.
3823    ///
3824    /// see also [`ui_draw_element`] [`ui_draw_element_color`]
3825    /// ### Examples
3826    /// ```
3827    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3828    /// use stereokit_rust::{ui::{Ui, UiVisual}, maths::{Vec2, Vec3, Pose}};
3829    ///
3830    /// let mut window_pose = Pose::new(
3831    ///     [0.01, 0.075, 0.9], Some([0.0, 185.0, 0.0].into()));
3832    ///
3833    /// filename_scr = "screenshots/ui_draw_element.jpeg";
3834    /// test_screenshot!( // !!!! Get a proper main loop !!!!
3835    ///     Ui::window_begin("Draw Element", &mut window_pose, Some([0.22, 0.18].into()), None, None);
3836    ///     Ui::draw_element(UiVisual::Button, None, [0.1, -0.01, 0.0], [0.1, 0.025, 0.005], 1.0);
3837    ///     Ui::draw_element(UiVisual::Input, None, [0.0, -0.01, 0.0], [0.1, 0.025, 0.005], 1.0);
3838    ///     Ui::draw_element(UiVisual::Handle, None, [0.1, -0.05, 0.0], [0.1, 0.025, 0.005], 1.0);
3839    ///     Ui::draw_element(UiVisual::Toggle, None, [0.0, -0.05, 0.0], [0.1, 0.025, 0.005], 1.0);
3840    ///     Ui::draw_element(UiVisual::Separator, None, [0.1, -0.08, 0.0], [0.2, 0.005, 0.005], 1.0);
3841    ///     Ui::draw_element(UiVisual::Aura, None, [0.1, -0.1, 0.0], [0.08, 0.08, 0.005], 0.5);
3842    ///     Ui::draw_element(UiVisual::Default, None, [0.0, -0.1, 0.0], [0.1, 0.025, 0.005], 0.0);
3843    ///     Ui::draw_element(UiVisual::Carat, None, [0.0, -0.14, 0.0], [0.025, 0.025, 0.005], 1.0);
3844    ///     Ui::window_end();
3845    /// );
3846    /// ```
3847    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_draw_element.jpeg" alt="screenshot" width="200">
3848    pub fn draw_element(
3849        element_visual: UiVisual,
3850        element_color: Option<UiVisual>,
3851        start: impl Into<Vec3>,
3852        size: impl Into<Vec3>,
3853        focus: f32,
3854    ) {
3855        match element_color {
3856            Some(element_color) => unsafe {
3857                ui_draw_element_color(element_visual, element_color, start.into(), size.into(), focus)
3858            },
3859            None => unsafe { ui_draw_element(element_visual, start.into(), size.into(), focus) },
3860        }
3861    }
3862
3863    /// This will get a final linear draw color for a particular UI element type with a particular focus value. This
3864    /// obeys the current hierarchy of tinting and enabled states.
3865    /// <https://stereokit.net/Pages/StereoKit/UI/GetElementColor.html>
3866    /// * `element_visual` - Get the color from this element type.  Use UiVisual::ExtraSlotXX to use extra
3867    ///   UiVisual slots for your own custom UI elements. If these slots are empty, SK will fall back to
3868    ///   UiVisual::Default.
3869    /// * `focus` - The amount of visual focus this element currently has, where 0 is unfocused, and 1 is active. You
3870    ///   can acquire a good focus value from `Ui::get_anim_focus`
3871    ///
3872    /// Returns a linear color good for tinting UI meshes.
3873    /// see also [`ui_get_element_color`]
3874    /// see example in [`Ui::set_element_color`]
3875    pub fn get_element_color(element_visual: UiVisual, focus: f32) -> Color128 {
3876        unsafe { ui_get_element_color(element_visual, focus) }
3877    }
3878
3879    /// This resolves a UI element with an ID and its current states into a nicely animated focus value.
3880    /// <https://stereokit.net/Pages/StereoKit/UI/GetAnimFocus.html>
3881    /// * `id` - The hierarchical id of the UI element we're checking the focus of, this can be created with
3882    ///   `Ui::stack_hash`.
3883    /// * `focus_state` - The current focus state of the UI element.
3884    /// * `activationState` - The current activation status of the/ UI element.
3885    ///
3886    /// Returns a focus value in the realm of 0-1, where 0 is unfocused, and 1 is active.
3887    /// see also [`ui_get_anim_focus`]
3888    /// ### Examples
3889    /// ```
3890    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3891    /// use stereokit_rust::{ui::Ui, system::BtnState, maths::{Vec2, Vec3, Pose}};
3892    ///
3893    /// let mut window_pose = Pose::new(
3894    ///     [0.01, 0.075, 0.9], Some([0.0, 185.0, 0.0].into()));
3895    ///
3896    /// test_steps!( // !!!! Get a proper main loop !!!!
3897    ///     Ui::window_begin("Get Anim Focus", &mut window_pose, None, None, None);
3898    ///     if Ui::button("button1", None) {todo!()}
3899    ///     let id = Ui::stack_hash("button1");
3900    ///     let focus = Ui::get_anim_focus(id, BtnState::Inactive, BtnState::Inactive);
3901    ///     assert_eq!(focus, 0.0);
3902    ///     let focus = Ui::get_anim_focus(id, BtnState::Active, BtnState::Inactive);
3903    ///     assert_eq!(focus, 0.5);
3904    ///     let focus = Ui::get_anim_focus(id, BtnState::Active, BtnState::Active);
3905    ///     assert_eq!(focus, 1.0);
3906    ///     Ui::window_end();
3907    /// );
3908    /// ```
3909    pub fn get_anim_focus(id: IdHashT, focus_state: BtnState, activation_state: BtnState) -> f32 {
3910        unsafe { ui_get_anim_focus(id, focus_state, activation_state) }
3911    }
3912
3913    /// This allows you to explicitly set a theme color, for finer grained control over the UI appearance. Each theme
3914    /// type is still used by many different UI elements. This will automatically generate colors for different UI
3915    /// element states.
3916    /// <https://stereokit.net/Pages/StereoKit/UI/SetThemeColor.html>
3917    /// * `color_category` - The category of UI elements that are affected by this theme color. Use UiColor::ExtraSlotXX
3918    ///   if you need extra UiColor slots for your own custom UI elements.
3919    /// * `color_state` - The state of the UI element this color should apply to. If None has the value
3920    ///   UiColorState::Normal
3921    /// * `color_gama` : the gamma corrected color that should be applied to this theme color category in its normal
3922    ///   resting state. Active and disabled colors will be generated based on this color.
3923    ///
3924    /// see also [`ui_set_theme_color`] [`ui_set_theme_color_state`] [`Ui::color_scheme`]
3925    /// ### Examples
3926    /// ```
3927    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
3928    /// use stereokit_rust::{ui::{Ui, UiColor, UiColorState}, maths::{Vec2, Vec3, Pose},
3929    ///                      util::{named_colors, Color128}};
3930    ///
3931    /// let mut window_pose = Pose::new(
3932    ///     [0.01, 0.04, 0.93], Some([0.0, 185.0, 0.0].into()));
3933    ///
3934    /// assert_eq!(Ui::get_theme_color(UiColor::Primary, None),
3935    ///            Color128 { r: 0.75, g: 0.5325, b: 0.375, a: 1.0 });
3936    ///
3937    /// let red: Color128 = named_colors::RED.into();
3938    /// Ui::set_theme_color(UiColor::Common, Some(UiColorState::Disabled), red.to_gamma());
3939    /// assert_eq!(Ui::get_theme_color(UiColor::Common,  Some(UiColorState::Disabled)),
3940    ///             red.to_gamma());
3941    ///
3942    /// let green: Color128 = named_colors::GREEN.into();
3943    /// Ui::set_theme_color(UiColor::Primary, None, green.to_gamma());
3944    /// assert_eq!(Ui::get_theme_color(UiColor::Primary, None),
3945    ///            green.to_gamma());
3946    ///
3947    /// let blue: Color128 = named_colors::BLUE.into();
3948    /// Ui::set_theme_color(UiColor::Background, None, blue.to_gamma());
3949    /// assert_eq!(Ui::get_theme_color(UiColor::Background, None),
3950    ///            blue.to_gamma());
3951    ///
3952    /// filename_scr = "screenshots/ui_set_theme_color.jpeg";
3953    /// test_screenshot!( // !!!! Get a proper main loop !!!!
3954    ///     Ui::window_begin("set_theme_color", &mut window_pose, None, None, None);
3955    ///     Ui::push_enabled(false, None);
3956    ///     if Ui::button("Button", None) { todo!() };
3957    ///     Ui::pop_enabled();
3958    ///     Ui::hseparator();
3959    ///     if Ui::button("Exit", None) {sk.quit(None);}
3960    ///     Ui::window_end();
3961    /// );
3962    /// ```
3963    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_set_theme_color.jpeg" alt="screenshot" width="200">
3964    pub fn set_theme_color(
3965        color_category: UiColor,
3966        color_state: Option<UiColorState>,
3967        color_gamma: impl Into<Color128>,
3968    ) {
3969        Log::diag(format!("set_theme_color for category: {:?}", unsafe {
3970            std::mem::transmute::<UiColor, u32>(color_category)
3971        }));
3972        match color_state {
3973            Some(color_state) => unsafe { ui_set_theme_color_state(color_category, color_state, color_gamma.into()) },
3974            None => unsafe { ui_set_theme_color(color_category, color_gamma.into()) },
3975        }
3976    }
3977
3978    /// This allows you to inspect the current color of the theme color category in a specific state! If you set the
3979    /// color with Ui::color_scheme, or without specifying a state, this may be a generated color, and not necessarily
3980    /// the color that was provided there.
3981    /// <https://stereokit.net/Pages/StereoKit/UI/GetThemeColor.html>
3982    /// * `color_category` - The category of UI elements that are affected by this theme color. Use UiColor::ExtraSlotXX
3983    ///   if you need extra UiColor slots for your own custom UI elements.
3984    ///   If the theme slot is empty, the color will be pulled from UiColor::None
3985    /// * `color_state` : The state of the UI element this color applies to. If None has the value UiColorState::Normal
3986    ///
3987    /// Returns the gamma space color for the theme color category in the indicated state.
3988    /// see also [`ui_get_theme_color`] [`ui_get_theme_color_state`]
3989    pub fn get_theme_color(color_category: UiColor, color_state: Option<UiColorState>) -> Color128 {
3990        match color_state {
3991            Some(color_state) => unsafe { ui_get_theme_color_state(color_category, color_state) },
3992            None => unsafe { ui_get_theme_color(color_category) },
3993        }
3994    }
3995
3996    /// adds some vertical space to the current line! All UI following elements on this line will be offset.
3997    /// <https://stereokit.net/Pages/StereoKit/UI/VSpace.html>
3998    /// * `space` - Space in meters to shift the layout by.
3999    ///
4000    /// see also [`ui_vspace`]
4001    /// ### Examples
4002    /// ```
4003    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4004    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
4005    ///
4006    /// let mut window_pose = Pose::new(
4007    ///     [0.01, 0.075, 0.88], Some([0.0, 185.0, 0.0].into()));
4008    ///
4009    /// test_steps!( // !!!! Get a proper main loop !!!!
4010    ///     Ui::window_begin("VSpace", &mut window_pose, None, None, None);
4011    ///     Ui::label("Line 1", None, false);
4012    ///     Ui::vspace(0.02);
4013    ///     Ui::label("Line 2", None, false);
4014    ///     Ui::vspace(0.04);
4015    ///     if Ui::button("Exit", None) {sk.quit(None);}
4016    ///     Ui::window_end();
4017    /// );
4018    /// ```
4019    pub fn vspace(space: f32) {
4020        unsafe { ui_vspace(space) }
4021    }
4022
4023    /// adds some horizontal space to the current line!
4024    /// <https://stereokit.net/Pages/StereoKit/UI/HSpace.html>
4025    /// * `space` - Space in meters to shift the layout by.
4026    ///
4027    /// see also [`ui_hspace`]
4028    /// ### Examples
4029    /// ```
4030    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4031    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
4032    ///
4033    /// let mut window_pose = Pose::new(
4034    ///     [0.01, 0.075, 0.88], Some([0.0, 185.0, 0.0].into()));
4035    ///
4036    /// test_steps!( // !!!! Get a proper main loop !!!!
4037    ///     Ui::window_begin("HSpace", &mut window_pose, None, None, None);
4038    ///     Ui::label("Bla bla ...", None, false);
4039    ///     Ui::same_line();
4040    ///     Ui::hspace(0.08);
4041    ///     if Ui::button("Exit", None) {sk.quit(None);}
4042    ///     Ui::window_end();
4043    /// );
4044    /// ```
4045    pub fn hspace(space: f32) {
4046        unsafe { ui_hspace(space) }
4047    }
4048
4049    /// This will hash the given text based id into a hash for use with certain StereoKit UI functions. This includes
4050    /// the hash of the current id stack.
4051    /// <https://stereokit.net/Pages/StereoKit/UI/StackHash.html>
4052    /// * `id` - Text to hash along with the current id stack.
4053    ///
4054    /// Returns an integer based hash id for use with SK UI.
4055    /// see also [`ui_stack_hash`]
4056    /// ### Examples
4057    /// ```
4058    /// use stereokit_rust::ui::Ui;
4059    ///
4060    /// let hash1 = Ui::stack_hash("button1");
4061    /// let hash2 = Ui::stack_hash("input2");
4062    ///
4063    /// assert_eq!(hash1, 17108023170974569920);
4064    /// assert_eq!(hash2, 5305247587935581291);
4065    /// ```
4066    pub fn stack_hash(id: impl AsRef<str>) -> IdHashT {
4067        let cstr = CString::new(id.as_ref()).unwrap();
4068        unsafe { ui_stack_hash(cstr.as_ptr()) }
4069    }
4070
4071    /// A scrolling text element! This is for reading large chunks of text that may be too long to fit in the available
4072    /// space when scroll is Some(size). It requires a height, as well as a place to store the current scroll value.
4073    /// Text uses the UI's current font settings, which can be changed with UI.Push/PopTextStyle.
4074    /// <https://stereokit.net/Pages/StereoKit/UI/Text.html>
4075    /// * `text` - The text you wish to display, there's no additional parsing done to this text, so put it in as you want
4076    ///   to see it!
4077    /// * `scroll` - This is the current scroll value of the text, in meters, _not_ percent.
4078    /// * `scrollDirection` - What scroll bars are allowed to show on this text? Vertical, horizontal, both? None is
4079    ///   UiScroll::None.
4080    /// * `height` - The vertical height of this Text element. None is 0.0.
4081    /// * `width` - if None it will automatically take the remainder of the current layout.
4082    /// * `text_align` - Where should the text position itself within its bounds? None is Align::TopLeft is how most
4083    ///   european language are aligned.
4084    /// * `fit` - Describe how the text should behave when one of its size dimensions conflicts with the provided ‘size’
4085    ///   parameter. None will use TextFit::Wrap by default and this scrolling overload will always add `TextFit.Clip`
4086    ///   internally.
4087    ///
4088    /// Returns true if any of the scroll bars have changed this frame.
4089    /// see also [`ui_text`]
4090    /// ### Examples
4091    /// ```
4092    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4093    /// use stereokit_rust::{ui::{Ui, UiScroll}, system::{Align, TextFit}, maths::{Vec2, Vec3, Pose}};
4094    ///
4095    /// let mut window_pose = Pose::new(
4096    ///     [0.01, 0.075, 0.9], Some([0.0, 185.0, 0.0].into()));
4097    ///
4098    /// let mut scroll_value = Vec2::new(0.05, 0.0);
4099    /// let mut scroll_value_at = Vec2::new(0.00, 0.165);
4100    /// let text = r#"Lorem ipsum dolor sit amet, consectetur
4101    /// adipiscing elit, sed do eiusmod tempor incididunt ut
4102    /// labore et dolore magna aliqua. Ut enim ad minim veniam,
4103    /// quis nostrud exercitation ullamco laboris nisi ut ... "#;
4104    ///
4105    /// filename_scr = "screenshots/ui_text.jpeg";
4106    /// test_screenshot!( // !!!! Get a proper main loop !!!!
4107    ///     Ui::window_begin("Text", &mut window_pose, Some([0.22, 0.14].into()), None, None);
4108    ///     Ui::text(text, Some(&mut scroll_value), Some(UiScroll::Both), Some(0.07), Some(0.21),
4109    ///              Some(Align::TopCenter), Some(TextFit::Clip));
4110    ///     Ui::text(text, None, None, Some(0.04), Some(1.8),
4111    ///              None, Some(TextFit::Exact));
4112    ///     Ui::text_at(text, Some(&mut scroll_value_at), Some(UiScroll::Both), Align::TopRight,
4113    ///                 TextFit::Wrap, [0.10, -0.14, 0.0], [0.21, 0.04]);
4114    ///     Ui::window_end();
4115    /// );
4116    /// ```
4117    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_text.jpeg" alt="screenshot" width="200">
4118    pub fn text(
4119        text: impl AsRef<str>,
4120        scroll: Option<&mut Vec2>,
4121        scroll_direction: Option<UiScroll>,
4122        height: Option<f32>,
4123        width: Option<f32>,
4124        text_align: Option<Align>,
4125        fit: Option<TextFit>,
4126    ) -> bool {
4127        let cstr = CString::new(text.as_ref()).unwrap();
4128        let scroll_direction = scroll_direction.unwrap_or(UiScroll::None);
4129        let height = height.unwrap_or(0.0);
4130        let text_align = text_align.unwrap_or(Align::TopLeft);
4131        let fit = fit.unwrap_or(TextFit::Wrap);
4132        if let Some(width) = width {
4133            let size = Vec2::new(width, height);
4134            match scroll {
4135                Some(scroll) => unsafe {
4136                    ui_text_sz(cstr.as_ptr(), scroll, scroll_direction, size, text_align, fit) != 0
4137                },
4138                None => unsafe { ui_text_sz(cstr.as_ptr(), null_mut(), UiScroll::None, size, text_align, fit) != 0 },
4139            }
4140        } else {
4141            match scroll {
4142                Some(scroll) => unsafe { ui_text(cstr.as_ptr(), scroll, scroll_direction, height, text_align) != 0 },
4143                None => unsafe { ui_text(cstr.as_ptr(), null_mut(), UiScroll::None, 0.0, text_align) != 0 },
4144            }
4145        }
4146    }
4147
4148    /// Displays a large chunk of text on the current layout. This can include new lines and spaces, and will properly
4149    /// wrap once it fills the entire layout! Text uses the UI’s current font settings, which can be changed with
4150    /// Ui::push/pop_text_style.
4151    /// <https://stereokit.net/Pages/StereoKit/UI/TextAt.html>
4152    /// * `text` - The text you wish to display, there's no additional parsing done to this text, so put it in as you want
4153    ///   to see it!
4154    /// * `scroll` - This is the current scroll value of the text, in meters, _not_ percent.
4155    /// * `scrollDirection` - What scroll bars are allowed to show on this text? Vertical, horizontal, both?
4156    /// * `text_align` - Where should the text position itself within its bounds?
4157    /// * `fit` - Describe how the text should behave when one of its size dimensions conflicts with the provided ‘size’
4158    ///   parameter.
4159    /// * `size` - The layout size for this element in Hierarchy space.
4160    ///
4161    /// Returns true if any of the scroll bars have changed this frame.
4162    /// see also [`ui_text_at`]
4163    /// see example in [`Ui::text`]
4164    pub fn text_at(
4165        text: impl AsRef<str>,
4166        scroll: Option<&mut Vec2>,
4167        scroll_direction: Option<UiScroll>,
4168        text_align: Align,
4169        fit: TextFit,
4170        top_left_corner: impl Into<Vec3>,
4171        size: impl Into<Vec2>,
4172    ) -> bool {
4173        let scroll_direction = scroll_direction.unwrap_or(UiScroll::None);
4174        let cstr = CString::new(text.as_ref()).unwrap();
4175        match scroll {
4176            Some(scroll) => unsafe {
4177                ui_text_at(
4178                    cstr.as_ptr(),
4179                    scroll,
4180                    scroll_direction,
4181                    text_align,
4182                    fit,
4183                    top_left_corner.into(),
4184                    size.into(),
4185                ) != 0
4186            },
4187            None => unsafe {
4188                ui_text_at(
4189                    cstr.as_ptr(),
4190                    null_mut(),
4191                    UiScroll::None,
4192                    text_align,
4193                    fit,
4194                    top_left_corner.into(),
4195                    size.into(),
4196                ) != 0
4197            },
4198        }
4199    }
4200
4201    /// A toggleable button! A button will expand to fit the text provided to it, vertically and horizontally. Text is
4202    /// re-used as the id. Will return the toggle value any time the toggle value changes or None if no change occurs
4203    /// <https://stereokit.net/Pages/StereoKit/UI/Toggle.html>
4204    /// * `text` - Text to display on the Toggle and id for tracking element state. MUST be unique within current
4205    ///   hierarchy.
4206    /// * `out_value` - The current state of the toggle button! True means it’s toggled on, and false means it’s
4207    ///   toggled off.
4208    /// * `size` - The layout size for this element in Hierarchy space. If an axis is left as zero, it will be
4209    ///   auto-calculated. For X this is the remaining width of the current layout, and for Y this is Ui::get_line_height.
4210    ///   None is for auto-calculated.
4211    ///
4212    /// Will return the new value (same as `out_value`) any time the toggle value changes.
4213    /// see also [`ui_toggle`] [`ui_toggle_sz`] [`Ui::toggle_img`] [`Ui::toggle_at`]
4214    /// ### Examples
4215    /// ```
4216    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4217    /// use stereokit_rust::{ui::{Ui, UiBtnLayout}, maths::{Vec2, Vec3, Pose}, sprite::Sprite};
4218    ///
4219    /// let mut window_pose = Pose::new(
4220    ///     [0.01, 0.065, 0.91], Some([0.0, 185.0, 0.0].into()));
4221    ///
4222    /// let (on, off) = (Sprite::arrow_up(), Sprite::arrow_down());
4223    ///
4224    /// let mut choiceA = false; let mut choiceB = true;
4225    /// let mut choiceC = false; let mut choiceD = true;
4226    /// let mut choiceE = false; let mut choiceF = true;
4227    ///
4228    /// filename_scr = "screenshots/ui_toggle.jpeg";
4229    /// test_screenshot!( // !!!! Get a proper main loop !!!!
4230    ///     Ui::window_begin("Toggle button", &mut window_pose, None, None, None);
4231    ///     Ui::toggle_img("A", &mut choiceA, &off, &on, Some(UiBtnLayout::Right),
4232    ///                    Some([0.06, 0.05].into()));
4233    ///     Ui::same_line();
4234    ///     if let Some(bool) = Ui::toggle_img("B", &mut choiceB, &off, &on,
4235    ///                                        Some(UiBtnLayout::Center),
4236    ///                                        Some([0.06, 0.05].into())) {todo!()}
4237    ///
4238    ///     Ui::toggle("C", &mut choiceC, None);
4239    ///     Ui::same_line();
4240    ///     Ui::toggle("D", &mut choiceD, Some([0.06, 0.04].into()));
4241    ///
4242    ///     Ui::toggle_at("E", &mut choiceE, Some(&off), None, Some(UiBtnLayout::Right),
4243    ///                     [0.06, -0.12, 0.0], [0.06, 0.03]);
4244    ///     if let Some(bool) = Ui::toggle_at("F", &mut choiceF, None, None, None,
4245    ///                                       [-0.01, -0.12, 0.0], [0.06, 0.03]) {todo!()}
4246    ///     Ui::window_end();
4247    /// );
4248    /// ```
4249    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_toggle.jpeg" alt="screenshot" width="200">
4250    pub fn toggle(text: impl AsRef<str>, out_value: &mut bool, size: Option<Vec2>) -> Option<bool> {
4251        let cstr = CString::new(text.as_ref()).unwrap();
4252        let mut active: Bool32T = *out_value as Bool32T;
4253        let active_ptr: *mut Bool32T = &mut active;
4254        let change = match size {
4255            Some(size) => unsafe { ui_toggle_sz(cstr.as_ptr(), active_ptr, size) != 0 },
4256            None => unsafe { ui_toggle(cstr.as_ptr(), active_ptr) != 0 },
4257        };
4258
4259        match change {
4260            true => {
4261                *out_value = active != 0;
4262                Some(*out_value)
4263            }
4264            false => None,
4265        }
4266    }
4267
4268    /// A toggleable button! A button will expand to fit the text provided to it, vertically and horizontally. Text is
4269    /// re-used as the id. Will return the toggle value any time the toggle value changes or None if no change occurs
4270    /// <https://stereokit.net/Pages/StereoKit/UI/Toggle.html>
4271    /// * `text` - Text to display on the Toggle and id for tracking element state. MUST be unique within current
4272    ///   hierarchy.
4273    /// * `out_value` - The current state of the toggle button! True means it’s toggled on, and false means it’s
4274    ///   toggled off.
4275    /// * `toggle_off` - Image to use when the toggle value is false.
4276    /// * `toggle_on` - Image to use when the toggle value is true.
4277    /// * `image_layout` - This enum specifies how the text and image should be laid out on the button. Default
4278    ///   [`UiBtnLayout::Left`]
4279    ///   will have the image on the left, and text on the right.
4280    /// * `size` - The layout size for this element in Hierarchy space. If an axis is left as zero, it will be
4281    ///   auto-calculated. For X this is the remaining width of the current layout, and for Y this is Ui::line_height.
4282    ///   None is for auto-calculated.
4283    ///
4284    /// Will return the new value (same as `out_value`) any time the toggle value changes.
4285    /// see also [`ui_toggle_img`] [`ui_toggle_img_sz`] [`Ui::toggle`] [`Ui::toggle_at`]
4286    /// see example in [`Ui::toggle`]
4287    pub fn toggle_img(
4288        id: impl AsRef<str>,
4289        out_value: &mut bool,
4290        toggle_off: impl AsRef<Sprite>,
4291        toggle_on: impl AsRef<Sprite>,
4292        image_layout: Option<UiBtnLayout>,
4293        size: Option<Vec2>,
4294    ) -> Option<bool> {
4295        let cstr = CString::new(id.as_ref()).unwrap();
4296        let mut active: Bool32T = *out_value as Bool32T;
4297        let active_ptr: *mut Bool32T = &mut active;
4298        let image_layout = image_layout.unwrap_or(UiBtnLayout::Left);
4299        let change = match size {
4300            Some(size) => unsafe {
4301                ui_toggle_img_sz(
4302                    cstr.as_ptr(),
4303                    active_ptr,
4304                    toggle_off.as_ref().0.as_ptr(),
4305                    toggle_on.as_ref().0.as_ptr(),
4306                    image_layout,
4307                    size,
4308                ) != 0
4309            },
4310            None => unsafe {
4311                ui_toggle_img(
4312                    cstr.as_ptr(),
4313                    active_ptr,
4314                    toggle_off.as_ref().0.as_ptr(),
4315                    toggle_on.as_ref().0.as_ptr(),
4316                    image_layout,
4317                ) != 0
4318            },
4319        };
4320        match change {
4321            true => {
4322                *out_value = active != 0;
4323                Some(*out_value)
4324            }
4325            false => None,
4326        }
4327    }
4328
4329    /// A variant of Ui::toggle that doesn’t use the layout system, and instead goes exactly where you put it.
4330    /// <https://stereokit.net/Pages/StereoKit/UI/ToggleAt.html>
4331    /// * `text` - Text to display on the Toggle and id for tracking element state. MUST be unique within current
4332    ///   hierarchy.
4333    /// * `out_value` - The current state of the toggle button! True means it’s toggled on, and false means it’s
4334    ///   toggled off.
4335    /// * `toggle_off`- Image to use when the toggle value is false or when no toggle-on image is specified.
4336    /// * `toggle_on` - Image to use when the toggle value is true and toggle-off has been specified. None will use
4337    ///   `toggle_off` image if it has been specified.
4338    /// * `imageLayout` - This enum specifies how the text and image should be laid out on the button.
4339    ///   None is [`UiBtnLayout::Left`] will have the image on the left, and text on the right.
4340    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
4341    /// * `size` - The layout size for this element in Hierarchy space.
4342    ///
4343    /// Will return the new value (same as `out_value`) any time the toggle value changes.
4344    /// see also [`ui_toggle_img_at`] [`ui_toggle_at`] [`Ui::toggle_img`] [`Ui::toggle`]
4345    /// see example in [`Ui::toggle`]
4346    pub fn toggle_at(
4347        id: impl AsRef<str>,
4348        out_value: &mut bool,
4349        toggle_off: Option<&Sprite>,
4350        toggle_on: Option<&Sprite>,
4351        image_layout: Option<UiBtnLayout>,
4352        top_left_corner: impl Into<Vec3>,
4353        size: impl Into<Vec2>,
4354    ) -> Option<bool> {
4355        let cstr = CString::new(id.as_ref()).unwrap();
4356        let mut active: Bool32T = *out_value as Bool32T;
4357        let active_ptr: *mut Bool32T = &mut active;
4358        let change = match toggle_off {
4359            Some(image_off) => {
4360                let image_layout = image_layout.unwrap_or(UiBtnLayout::Left);
4361                let sprite_off = image_off.0.as_ptr();
4362                let image_on = toggle_on.unwrap_or(image_off);
4363                unsafe {
4364                    ui_toggle_img_at(
4365                        cstr.as_ptr(),
4366                        active_ptr as *mut Bool32T,
4367                        sprite_off,
4368                        image_on.0.as_ptr(),
4369                        image_layout,
4370                        top_left_corner.into(),
4371                        size.into(),
4372                    ) != 0
4373                }
4374            }
4375            None => unsafe { ui_toggle_at(cstr.as_ptr(), active_ptr, top_left_corner.into(), size.into()) != 0 },
4376        };
4377        match change {
4378            true => {
4379                *out_value = active != 0;
4380                Some(*out_value)
4381            }
4382            false => None,
4383        }
4384    }
4385
4386    /// A volume for helping to build one handed interactions. This checks for the presence of a hand inside the bounds,
4387    /// and if found, return that hand along with activation and focus information defined by the interactType.
4388    /// <https://stereokit.net/Pages/StereoKit/UI/VolumeAt.html>
4389    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
4390    /// * `bounds` - Size and position of the volume, relative to the current Hierarchy.
4391    /// * `interact_type` - `UiConfirm::Pinch` will activate when the hand performs a ‘pinch’ gesture. `UiConfirm::Push`
4392    ///   will activate when the hand enters the volume, and behave the same as element’s focusState.
4393    /// * `out_hand` - This will be the last unpreoccupied hand found inside the volume, and is the hand controlling the
4394    ///   interaction.
4395    /// * `out_focusState` - The focus state tells if the element has a hand inside of the volume that qualifies for focus.
4396    ///
4397    /// see also [`ui_volume_at`]
4398    /// ### Examples
4399    /// ```
4400    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4401    /// use stereokit_rust::{ui::{Ui, UiConfirm}, maths::{Vec2, Vec3, Pose, Bounds},
4402    ///                      system::{Hand, Handed, BtnState}};
4403    ///
4404    /// let mut window_pose = Pose::new(
4405    ///     [0.01, 0.075, 0.9], Some([0.0, 185.0, 0.0].into()));
4406    ///
4407    /// let bounds = Bounds::new([0.0, -0.05, 0.0], [0.05, 0.05, 0.05]);
4408    ///
4409    /// let mut hand_volume = Handed::Max;
4410    /// let mut focus_state = BtnState::Inactive;
4411    ///
4412    /// test_steps!( // !!!! Get a proper main loop !!!!
4413    ///     Ui::window_begin("Volume At", &mut window_pose, None, None, None);
4414    ///     let is_active = Ui::volume_at("volume", bounds, UiConfirm::Push,
4415    ///                                   Some(&mut hand_volume), Some(&mut focus_state));
4416    ///     assert_eq!(is_active, BtnState::Inactive);
4417    ///
4418    ///     let is_active = Ui::volume_at("volume", bounds, UiConfirm::Pinch,
4419    ///                                   None, None);
4420    ///     assert_eq!(is_active, BtnState::Inactive);
4421    ///     Ui::window_end();
4422    /// );
4423    /// ```
4424    pub fn volume_at(
4425        id: impl AsRef<str>,
4426        bounds: impl Into<Bounds>,
4427        interact_type: UiConfirm,
4428        out_hand: Option<*mut Handed>,
4429        out_focus_state: Option<*mut BtnState>,
4430    ) -> BtnState {
4431        let cstr = CString::new(id.as_ref()).unwrap();
4432        let hand = out_hand.unwrap_or(null_mut());
4433        let focus_state = out_focus_state.unwrap_or(null_mut());
4434        unsafe { ui_volume_at(cstr.as_ptr(), bounds.into(), interact_type, hand, focus_state) }
4435    }
4436
4437    /// A vertical slider element! You can stick your finger in it, and slide the value up and down.
4438    /// <https://stereokit.net/Pages/StereoKit/UI/VSlider.html>
4439    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
4440    /// * `out_value` - The value that the slider will store slider state in.
4441    /// * `min` - The minimum value the slider can set, left side of the slider.
4442    /// * `max` - The maximum value the slider can set, right side of the slider.
4443    /// * `step` - Locks the value to increments of step. Starts at min, and increments by step. None is default 0
4444    ///   and means "don't lock to increments".
4445    /// * `height` - Physical height of the slider on the window. None is default 0 will fill the remaining amount of
4446    ///   window space.
4447    /// * `confirm_method` - How should the slider be activated? None is default Push will be a push-button the user
4448    ///   must press first, and pinch will be a tab that the user must pinch and drag around.
4449    /// * `notify_on` - Allows you to modify the behavior of the return value. None is default UiNotify::Change.
4450    ///
4451    /// Returns new value of the slider if it has changed during this step.
4452    /// see also [`ui_vslider`] [`Ui::vslider_f64`] [`Ui::vslider_at`]  [`Ui::vslider_at_f64`]
4453    /// ### Examples
4454    /// ```
4455    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4456    /// use stereokit_rust::{ui::{Ui, UiConfirm, UiNotify}, maths::{Vec2, Vec3, Pose}};
4457    ///
4458    /// let mut window_pose = Pose::new(
4459    ///     [0.01, 0.055, 0.91], Some([0.0, 185.0, 0.0].into()));
4460    ///
4461    /// let mut scaling1 = 0.15;
4462    /// let mut scaling2 = 0.50f64;
4463    /// let mut scaling3 = 0.0;
4464    /// let mut scaling4 = 0.85;
4465    ///
4466    /// filename_scr = "screenshots/ui_vslider.jpeg";
4467    /// test_screenshot!( // !!!! Get a proper main loop !!!!
4468    ///     Ui::window_begin("HSlider", &mut window_pose, Some([0.18, 0.14].into()), None, None);
4469    ///     Ui::vslider(    "scaling1", &mut scaling1, 0.0, 1.0, Some(0.05), Some(0.10),
4470    ///                     None, None);
4471    ///     Ui::same_line();
4472    ///     Ui::vslider_f64("scaling2", &mut scaling2, 0.0, 1.0, None, Some(0.12),
4473    ///                     Some(UiConfirm::Pinch), None);
4474    ///
4475    ///     Ui::vslider_at( "scaling3", &mut scaling3, 0.0, 1.0, None,
4476    ///                     [-0.01, -0.01, 0.0], [0.02, 0.08],
4477    ///                     None, Some(UiNotify::Finalize));
4478    ///     if let Some(new_value) = Ui::vslider_at_f64(
4479    ///                     "scaling4", &mut scaling4, 0.0, 1.0, None,
4480    ///                     [-0.05, -0.01, 0.0], [0.036, 0.15],
4481    ///                     Some(UiConfirm::VariablePinch), None) {
4482    ///         if new_value == 1.0 {
4483    ///             Log::info("scaling4 is at max");
4484    ///         }
4485    ///     }
4486    ///     Ui::window_end();
4487    /// );
4488    /// ```
4489    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_vslider.jpeg" alt="screenshot" width="200">
4490    #[allow(clippy::too_many_arguments)]
4491    pub fn vslider(
4492        id: impl AsRef<str>,
4493        value: &mut f32,
4494        min: f32,
4495        max: f32,
4496        step: Option<f32>,
4497        height: Option<f32>,
4498        confirm_method: Option<UiConfirm>,
4499        notify_on: Option<UiNotify>,
4500    ) -> Option<f32> {
4501        let cstr = CString::new(id.as_ref()).unwrap();
4502        let step = step.unwrap_or(0.0);
4503        let height = height.unwrap_or(0.0);
4504        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
4505        let notify_on = notify_on.unwrap_or(UiNotify::Change);
4506        match unsafe { ui_vslider(cstr.as_ptr(), value, min, max, step, height, confirm_method, notify_on) != 0 } {
4507            true => Some(*value),
4508            false => None,
4509        }
4510    }
4511
4512    /// A vertical slider element! You can stick your finger in it, and slide the value up and down.
4513    /// <https://stereokit.net/Pages/StereoKit/UI/VSlider.html>
4514    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
4515    /// * `out_value` - The value that the slider will store slider state in.
4516    /// * `min` - The minimum value the slider can set, left side of the slider.
4517    /// * `max` - The maximum value the slider can set, right side of the slider.
4518    /// * `step` - Locks the value to increments of step. Starts at min, and increments by step. None is default 0
4519    ///   and means "don't lock to increments".
4520    /// * `height` - Physical height of the slider on the window. None is default 0 will fill the remaining amount of
4521    ///   window space.
4522    /// * `confirm_method` - How should the slider be activated? None is default Push will be a push-button the user
4523    ///   must press first, and pinch will be a tab that the user must pinch and drag around.
4524    /// * `notify_on` - Allows you to modify the behavior of the return value. None is default UiNotify::Change.
4525    ///
4526    /// Returns new value of the slider if it has changed during this step.
4527    /// see also [`ui_vslider_f64`] [`Ui::vslider`] [`Ui::vslider_at`]  [`Ui::vslider_at_f64`]
4528    /// see example in [`Ui::vslider`]
4529    #[allow(clippy::too_many_arguments)]
4530    pub fn vslider_f64(
4531        id: impl AsRef<str>,
4532        value: &mut f64,
4533        min: f64,
4534        max: f64,
4535        step: Option<f64>,
4536        height: Option<f32>,
4537        confirm_method: Option<UiConfirm>,
4538        notify_on: Option<UiNotify>,
4539    ) -> Option<f64> {
4540        let cstr = CString::new(id.as_ref()).unwrap();
4541        let step = step.unwrap_or(0.0);
4542        let height = height.unwrap_or(0.0);
4543        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
4544        let notify_on = notify_on.unwrap_or(UiNotify::Change);
4545        match unsafe { ui_vslider_f64(cstr.as_ptr(), value, min, max, step, height, confirm_method, notify_on) != 0 } {
4546            true => Some(*value),
4547            false => None,
4548        }
4549    }
4550
4551    /// A vertical slider element! You can stick your finger in it, and slide the value up and down.
4552    /// <https://stereokit.net/Pages/StereoKit/UI/VSliderAt.html>
4553    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
4554    /// * `out_value` - The value that the slider will store slider state in.
4555    /// * `min` - The minimum value the slider can set, left side of the slider.
4556    /// * `max` - The maximum value the slider can set, right side of the slider.
4557    /// * `step` - Locks the value to increments of step. Starts at min, and increments by step. None is default 0
4558    ///   and means "don't lock to increments".
4559    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
4560    /// * `size` - The layout size for this element in Hierarchy space.
4561    /// * `confirm_method` - How should the slider be activated? None is default Push will be a push-button the user
4562    ///   must press first, and pinch will be a tab that the user must pinch and drag around.
4563    /// * `notify_on` - Allows you to modify the behavior of the return value. None is default UiNotify::Change.
4564    ///
4565    /// Returns new value of the slider if it has changed during this step.
4566    /// see also [`ui_vslider_at`] [`Ui::vslider`] [`Ui::vslider_f64`]  [`Ui::vslider_at_f64`]
4567    /// see example in [`Ui::vslider`]
4568    #[allow(clippy::too_many_arguments)]
4569    pub fn vslider_at(
4570        id: impl AsRef<str>,
4571        value: &mut f32,
4572        min: f32,
4573        max: f32,
4574        step: Option<f32>,
4575        top_left_corner: impl Into<Vec3>,
4576        size: impl Into<Vec2>,
4577        confirm_method: Option<UiConfirm>,
4578        notify_on: Option<UiNotify>,
4579    ) -> Option<f32> {
4580        let cstr = CString::new(id.as_ref()).unwrap();
4581        let step = step.unwrap_or(0.0);
4582        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
4583        let notify_on = notify_on.unwrap_or(UiNotify::Change);
4584        match unsafe {
4585            ui_vslider_at(
4586                cstr.as_ptr(),
4587                value,
4588                min,
4589                max,
4590                step,
4591                top_left_corner.into(),
4592                size.into(),
4593                confirm_method,
4594                notify_on,
4595            ) != 0
4596        } {
4597            true => Some(*value),
4598            false => None,
4599        }
4600    }
4601
4602    /// A vertical slider element! You can stick your finger in it, and slide the value up and down.
4603    /// <https://stereokit.net/Pages/StereoKit/UI/VSliderAt.html>
4604    /// * `id` - An id for tracking element state. MUST be unique within current hierarchy.
4605    /// * `out_value` - The value that the slider will store slider state in.
4606    /// * `min` - The minimum value the slider can set, left side of the slider.
4607    /// * `max` - The maximum value the slider can set, right side of the slider.
4608    /// * `step` - Locks the value to increments of step. Starts at min, and increments by step. None is default 0
4609    ///   and means "don't lock to increments".
4610    /// * `top_left_corner` - This is the top left corner of the UI element relative to the current Hierarchy.
4611    /// * `size` - The layout size for this element in Hierarchy space.
4612    /// * `confirm_method` - How should the slider be activated? None is default Push will be a push-button the user
4613    ///   must press first, and pinch will be a tab that the user must pinch and drag around.
4614    /// * `notify_on` - Allows you to modify the behavior of the return value. None is default UiNotify::Change.
4615    ///
4616    /// Returns new value of the slider if it has changed during this step.
4617    /// see also [`ui_vslider_at_f64`] [`Ui::vslider`] [`Ui::vslider_at`]  [`Ui::vslider_f64`]
4618    /// see example in [`Ui::vslider`]
4619    #[allow(clippy::too_many_arguments)]
4620    pub fn vslider_at_f64(
4621        id: impl AsRef<str>,
4622        value: &mut f64,
4623        min: f64,
4624        max: f64,
4625        step: Option<f64>,
4626        top_left_corner: impl Into<Vec3>,
4627        size: impl Into<Vec2>,
4628        confirm_method: Option<UiConfirm>,
4629        notify_on: Option<UiNotify>,
4630    ) -> Option<f64> {
4631        let cstr = CString::new(id.as_ref()).unwrap();
4632        let step = step.unwrap_or(0.0);
4633        let confirm_method = confirm_method.unwrap_or(UiConfirm::Push);
4634        let notify_on = notify_on.unwrap_or(UiNotify::Change);
4635        match unsafe {
4636            ui_vslider_at_f64(
4637                cstr.as_ptr(),
4638                value,
4639                min,
4640                max,
4641                step,
4642                top_left_corner.into(),
4643                size.into(),
4644                confirm_method,
4645                notify_on,
4646            ) != 0
4647        } {
4648            true => Some(*value),
4649            false => None,
4650        }
4651    }
4652
4653    /// Begins a new window! This will push a pose onto the transform stack, and all UI elements will be relative to
4654    /// that new pose. The pose is actually the top-center of the window. Must be finished with a call to
4655    /// Ui::window_end().
4656    /// If size is None the size will be auto-calculated based on the content provided during the previous frame.
4657    /// <https://stereokit.net/Pages/StereoKit/UI/WindowBegin.html>
4658    /// * `text` - Text to display on the window title and id for tracking element state. MUST be unique within current
4659    ///   hierarchy.
4660    /// * `pose` - The pose state for the window! If showHeader is true, the user will be able to grab this header and
4661    ///   move it around.
4662    /// * `size` - Physical size of the window! If None, then the size on that axis will be auto-
4663    ///   calculated based on the content provided during the previous frame.
4664    /// * `windowType` - Describes how the window should be drawn, use a header, a body, neither, or both? None is
4665    ///   UiWin::Normal
4666    /// * `moveType` - Describes how the window will move when dragged around. None is UiMove::FaceUser
4667    ///
4668    /// see also [`ui_window_begin`]
4669    /// ### Examples
4670    /// ```
4671    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4672    /// use stereokit_rust::{ui::{Ui, UiMove, UiWin}, maths::{Vec2, Vec3, Pose}};
4673    ///
4674    /// let mut window_pose1 = Pose::new(
4675    ///     [-0.07, 0.115, 0.89], Some([0.0, 185.0, 0.0].into()));
4676    /// let mut window_pose2 = Pose::new(
4677    ///     [-0.05, 0.02, 0.89], Some([0.0, 180.0, 0.0].into()));
4678    /// let mut window_pose3 = Pose::new(
4679    ///     [0.09, -0.075, 0.89], Some([0.0, 175.0, 0.0].into()));
4680    ///
4681    /// filename_scr = "screenshots/ui_window.jpeg";
4682    /// test_screenshot!( // !!!! Get a proper main loop !!!!
4683    ///     Ui::window_begin("Window", &mut window_pose1, None, Some(UiWin::Body), None);
4684    ///     Ui::label("Hello", None, true);
4685    ///     Ui::window_end();
4686    ///
4687    ///     Ui::window_begin("Window", &mut window_pose2, Some([0.19, 0.05].into()), None, None);
4688    ///     Ui::label("World", None, true);
4689    ///     Ui::window_end();
4690    ///
4691    ///     Ui::window_begin("Window", &mut window_pose3, None, None, Some(UiMove::Exact));
4692    ///     Ui::label("!!", None, true);
4693    ///     Ui::window_end();
4694    /// );
4695    /// ```
4696    /// <img src="https://raw.githubusercontent.com/mvvvv/StereoKit-rust/refs/heads/master/screenshots/ui_window.jpeg" alt="screenshot" width="200">
4697    pub fn window_begin(
4698        text: impl AsRef<str>,
4699        pose: &mut Pose,
4700        size: Option<Vec2>,
4701        window_type: Option<UiWin>,
4702        move_type: Option<UiMove>,
4703    ) {
4704        let cstr = CString::new(text.as_ref()).unwrap();
4705        let window_type = window_type.unwrap_or(UiWin::Normal);
4706        let move_type = move_type.unwrap_or(UiMove::FaceUser);
4707        let size = size.unwrap_or(Vec2::ZERO);
4708        unsafe { ui_window_begin(cstr.as_ptr(), pose, size, window_type, move_type) }
4709    }
4710
4711    /// Finishes a window! Must be called after Ui::window_begin() and all elements have been drawn.
4712    /// <https://stereokit.net/Pages/StereoKit/UI/WindowEnd.html>
4713    ///
4714    /// see also [`ui_window_end`]
4715    /// see example in [`Ui::window_begin`]
4716    pub fn window_end() {
4717        unsafe { ui_window_end() }
4718    }
4719
4720    /// get the flag about the far ray grab interaction for Handle elements like the Windows. It can be enabled and
4721    /// disabled for individual UI elements, and if this remains disabled at the start of the next frame, then the
4722    /// hand ray indicators will not be visible. This is enabled by default.
4723    /// <https://stereokit.net/Pages/StereoKit/UI/EnableFarInteract.html>
4724    ///
4725    /// see also [`ui_far_interact_enabled`]
4726    /// see example in [`Ui::enable_far_interact`]
4727    pub fn get_enable_far_interact() -> bool {
4728        unsafe { ui_far_interact_enabled() != 0 }
4729    }
4730
4731    /// Tells the Active state of the most recently called UI element that used an id.
4732    /// <https://stereokit.net/Pages/StereoKit/UI/LastElementActive.html>
4733    ///
4734    /// see also [`ui_last_element_active`]
4735    /// see example in [`Ui::last_element_hand_active`]
4736    pub fn get_last_element_active() -> BtnState {
4737        unsafe { ui_last_element_active() }
4738    }
4739
4740    /// Tells the Focused state of the most recently called UI element that used an id.
4741    /// <https://stereokit.net/Pages/StereoKit/UI/LastElementFocused.html>
4742    ///
4743    /// see also [`ui_last_element_focused`]
4744    /// see example in [`Ui::last_element_hand_focused`]
4745    pub fn get_last_element_focused() -> BtnState {
4746        unsafe { ui_last_element_focused() }
4747    }
4748
4749    /// The hierarchy local position of the current UI layout position. The top left point of the next UI element will
4750    /// be start here!
4751    /// <https://stereokit.net/Pages/StereoKit/UI/LayoutAt.html>
4752    ///
4753    /// see also [`ui_layout_at`]
4754    /// see example in [`Ui::panel_at`]
4755    pub fn get_layout_at() -> Vec3 {
4756        unsafe { ui_layout_at() }
4757    }
4758
4759    /// These are the layout bounds of the most recently reserved layout space. The Z axis dimensions are always 0.
4760    /// Only UI elements that affect the surface’s layout will report their bounds here. You can reserve your own layout
4761    /// space via Ui::layout_reserve, and that call will also report here.
4762    /// <https://stereokit.net/Pages/StereoKit/UI/LayoutLast.html>
4763    ///
4764    /// see also [`ui_layout_last`]
4765    /// ### Examples TODO: Very very slow under Windows
4766    /// ```no_run
4767    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4768    /// use stereokit_rust::{ui::{Ui, UiPad, UiCut}, maths::{Vec2, Vec3, Pose, Bounds}};
4769    ///
4770    /// let mut window_pose = Pose::new(
4771    ///     [0.01, 0.055, 0.90], Some([0.0, 185.0, 0.0].into()));
4772    ///
4773    /// test_steps!( // !!!! Get a proper main loop !!!!
4774    ///     Ui::window_begin("Panel at", &mut window_pose, Some([0.2, 0.15].into()), None, None);
4775    ///     Ui::panel_at([0.11, -0.01, 0.0], [0.08, 0.03], Some(UiPad::None));
4776    ///     Ui::label("panel 1", None, false);
4777    ///
4778    ///     Ui::layout_push_cut( UiCut::Right, 0.1, true);
4779    ///     Ui::panel_at(Ui::get_layout_at(), Ui::get_layout_remaining(), None);
4780    ///     Ui::label("panel 2", None, false);
4781    ///     Ui::layout_pop();
4782    ///     assert_eq!(Ui::get_layout_last(),
4783    ///         Bounds { center: Vec3 { x: -0.02382, y: -0.035, z: 0.0 },
4784    ///                  dimensions: Vec3 { x: 0.06765, y: 0.05, z: 0.0 } });
4785    ///
4786    ///     Ui::layout_push_cut( UiCut::Bottom, 0.08, false);
4787    ///     Ui::panel_at(Ui::get_layout_at(), Ui::get_layout_remaining(), None);
4788    ///     Ui::label("panel 3", None, false);
4789    ///     Ui::layout_pop();
4790    ///     assert_eq!(Ui::get_layout_last(),
4791    ///         Bounds { center: Vec3 { x: 0.0661, y: -0.075, z: 0.0 },
4792    ///                  dimensions: Vec3 { x: 0.0476, y: 0.03, z: 0.0 } });    
4793    ///
4794    ///     Ui::window_end();
4795    /// );
4796    /// ```
4797    pub fn get_layout_last() -> Bounds {
4798        unsafe { ui_layout_last() }
4799    }
4800
4801    /// How much space is available on the current layout! This is based on the current layout position, so X will give
4802    /// you the amount remaining on the current line, and Y will give you distance to the bottom of the layout,
4803    /// including the current line. These values will be 0 if you’re using 0 for the layout size on that axis.
4804    /// <https://stereokit.net/Pages/StereoKit/UI/LayoutRemaining.html>
4805    ///
4806    /// see also [`ui_layout_remaining`]
4807    /// see example in [`Ui::panel_at`]
4808    pub fn get_layout_remaining() -> Vec2 {
4809        unsafe { ui_layout_remaining() }
4810    }
4811
4812    /// This is the height of a single line of text with padding in the UI’s layout system!
4813    /// <https://stereokit.net/Pages/StereoKit/UI/LineHeight.html>
4814    ///
4815    /// see also [`ui_line_height`]
4816    /// ### Examples
4817    /// ```
4818    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
4819    /// use stereokit_rust::{ui::Ui, maths::{Vec2, Vec3, Pose}};
4820    ///
4821    /// let line_height = Ui::get_line_height();
4822    /// assert_eq!(line_height, 0.030000001);
4823    /// ```
4824    pub fn get_line_height() -> f32 {
4825        unsafe { ui_line_height() }
4826    }
4827
4828    /// UI sizing and layout settings.
4829    /// <https://stereokit.net/Pages/StereoKit/UI/Settings.html>
4830    ///
4831    /// see also [`ui_get_settings`]
4832    /// see example in [`Ui::settings`]
4833    pub fn get_settings() -> UiSettings {
4834        unsafe { ui_get_settings() }
4835    }
4836
4837    /// This is the UiMove that is provided to UI windows that StereoKit itself manages, such as the fallback
4838    /// filepicker and soft keyboard.
4839    /// <https://stereokit.net/Pages/StereoKit/UI/SystemMoveType.html>
4840    ///
4841    /// see also [`ui_system_get_move_type`]
4842    /// see example in [`Ui::system_move_type`]
4843    pub fn get_system_move_type() -> UiMove {
4844        unsafe { ui_system_get_move_type() }
4845    }
4846
4847    /// This returns the TextStyle that’s on top of the UI’s stack, according to Ui::(push/pop)_text_style.
4848    /// <https://stereokit.net/Pages/StereoKit/UI/TextStyle.html>
4849    ///
4850    /// see also [`ui_get_text_style`]
4851    /// see example in [`Ui::push_text_style`]
4852    pub fn get_text_style() -> TextStyle {
4853        unsafe { ui_get_text_style() }
4854    }
4855
4856    /// This returns the current state of the UI's enabled status stack, set by `Ui::(push/pop)_enabled`.
4857    /// <https://stereokit.net/Pages/StereoKit/UI/Enabled.html>
4858    ///
4859    /// see also [`ui_is_enabled`]
4860    /// see example in [`Ui::push_enabled`]
4861    pub fn get_enabled() -> bool {
4862        unsafe { ui_is_enabled() != 0 }
4863    }
4864}