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}