#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct PixelRect {
pub x: i32,
pub y: i32,
pub w: u32,
pub h: u32,
}
impl PixelRect {
pub const fn new(x: i32, y: i32, w: u32, h: u32) -> Self {
Self { x, y, w, h }
}
pub const fn is_non_empty(&self) -> bool {
self.w > 0 && self.h > 0
}
pub fn contains(&self, px: f32, py: f32) -> bool {
px >= self.x as f32
&& py >= self.y as f32
&& px < (self.x + self.w as i32) as f32
&& py < (self.y + self.h as i32) as f32
}
pub fn clip(&self, child: &PixelRect) -> PixelRect {
let x0 = self.x.max(child.x);
let y0 = self.y.max(child.y);
let x1 = (self.x + self.w as i32).min(child.x + child.w as i32);
let y1 = (self.y + self.h as i32).min(child.y + child.h as i32);
if x1 <= x0 || y1 <= y0 {
return PixelRect::default();
}
PixelRect {
x: x0,
y: y0,
w: (x1 - x0) as u32,
h: (y1 - y0) as u32,
}
}
}
use crate::Color;
#[derive(Clone, Debug)]
pub struct UiStyle {
pub background: Color,
pub surface: Color,
pub accent: Color,
pub text: Color,
pub text_secondary: Color,
pub border: Color,
pub corner_radius: f32,
pub padding: f32,
pub font_size: f32,
}
impl Default for UiStyle {
fn default() -> Self {
Self {
background: Color::new(0.082, 0.082, 0.090, 1.0),
surface: Color::new(0.141, 0.141, 0.161, 1.0),
accent: Color::new(0.388, 0.400, 0.945, 1.0),
text: Color::new(0.961, 0.961, 0.961, 1.0),
text_secondary: Color::new(0.600, 0.600, 0.620, 1.0),
border: Color::new(0.250, 0.250, 0.280, 1.0),
corner_radius: 4.0,
padding: 8.0,
font_size: 14.0,
}
}
}
impl UiStyle {
pub fn viewer_dark() -> Self {
Self {
background: Color::new(0.055, 0.055, 0.060, 1.0),
..Self::default()
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub enum UiSizing {
#[default]
Fill,
Fixed(f32),
Hug,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct UiEdge {
pub top: f32,
pub right: f32,
pub bottom: f32,
pub left: f32,
}
impl UiEdge {
pub const fn all(v: f32) -> Self {
Self {
top: v,
right: v,
bottom: v,
left: v,
}
}
}
#[derive(Clone, Copy, Debug, Default)]
pub enum UiDirection {
#[default]
Column,
Row,
}
#[derive(Clone, Copy, Debug, Default)]
pub enum UiAlign {
Start,
Center,
End,
#[default]
Stretch,
}
#[derive(Clone, Debug)]
pub struct UiLayout {
pub direction: UiDirection,
pub align: UiAlign,
pub gap: f32,
pub padding: UiEdge,
pub width: UiSizing,
pub height: UiSizing,
}
impl Default for UiLayout {
fn default() -> Self {
Self {
direction: UiDirection::Column,
align: UiAlign::Stretch,
gap: 4.0,
padding: UiEdge::all(8.0),
width: UiSizing::Fill,
height: UiSizing::Hug,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum UiKey {
#[default]
Unknown = 0,
A = 4,
B = 5,
C = 6,
D = 7,
E = 8,
F = 9,
G = 10,
H = 11,
I = 12,
J = 13,
K = 14,
L = 15,
M = 16,
N = 17,
O = 18,
P = 19,
Q = 20,
R = 21,
S = 22,
T = 23,
U = 24,
V = 25,
W = 26,
X = 27,
Y = 28,
Z = 29,
Num1 = 30,
Num2 = 31,
Num3 = 32,
Num4 = 33,
Num5 = 34,
Num6 = 35,
Num7 = 36,
Num8 = 37,
Num9 = 38,
Num0 = 39,
Return = 40,
Escape = 41,
Backspace = 42,
Tab = 43,
Space = 44,
Minus = 45,
Equals = 46,
Comma = 54,
Period = 55,
Slash = 56,
F1 = 58,
F2 = 59,
F3 = 60,
F4 = 61,
F5 = 62,
F6 = 63,
F7 = 64,
F8 = 65,
F9 = 66,
F10 = 67,
F11 = 68,
F12 = 69,
Home = 74,
Delete = 76,
End = 77,
Right = 79,
Left = 80,
Down = 81,
Up = 82,
KpEnter = 88,
Kp1 = 89,
Kp2 = 90,
Kp3 = 91,
Kp4 = 92,
Kp5 = 93,
Kp6 = 94,
Kp7 = 95,
Kp8 = 96,
Kp9 = 97,
Kp0 = 98,
}
impl UiKey {
pub fn from_sdl_scancode(scancode: u32) -> Self {
match scancode {
4 => Self::A,
5 => Self::B,
6 => Self::C,
7 => Self::D,
8 => Self::E,
9 => Self::F,
10 => Self::G,
11 => Self::H,
12 => Self::I,
13 => Self::J,
14 => Self::K,
15 => Self::L,
16 => Self::M,
17 => Self::N,
18 => Self::O,
19 => Self::P,
20 => Self::Q,
21 => Self::R,
22 => Self::S,
23 => Self::T,
24 => Self::U,
25 => Self::V,
26 => Self::W,
27 => Self::X,
28 => Self::Y,
29 => Self::Z,
30 => Self::Num1,
31 => Self::Num2,
32 => Self::Num3,
33 => Self::Num4,
34 => Self::Num5,
35 => Self::Num6,
36 => Self::Num7,
37 => Self::Num8,
38 => Self::Num9,
39 => Self::Num0,
40 => Self::Return,
41 => Self::Escape,
42 => Self::Backspace,
43 => Self::Tab,
44 => Self::Space,
45 => Self::Minus,
46 => Self::Equals,
54 => Self::Comma,
55 => Self::Period,
56 => Self::Slash,
58 => Self::F1,
59 => Self::F2,
60 => Self::F3,
61 => Self::F4,
62 => Self::F5,
63 => Self::F6,
64 => Self::F7,
65 => Self::F8,
66 => Self::F9,
67 => Self::F10,
68 => Self::F11,
69 => Self::F12,
74 => Self::Home,
76 => Self::Delete,
77 => Self::End,
79 => Self::Right,
80 => Self::Left,
81 => Self::Down,
82 => Self::Up,
88 => Self::KpEnter,
89 => Self::Kp1,
90 => Self::Kp2,
91 => Self::Kp3,
92 => Self::Kp4,
93 => Self::Kp5,
94 => Self::Kp6,
95 => Self::Kp7,
96 => Self::Kp8,
97 => Self::Kp9,
98 => Self::Kp0,
_ => Self::Unknown,
}
}
pub fn is_arrow(self) -> bool {
matches!(self, Self::Left | Self::Right | Self::Up | Self::Down)
}
}
pub type UiModifiers = u32;
pub const UI_MOD_NONE: UiModifiers = 0;
pub const UI_MOD_SHIFT: UiModifiers = 1 << 0;
pub const UI_MOD_CTRL: UiModifiers = 1 << 1;
pub const UI_MOD_ALT: UiModifiers = 1 << 2;
pub const UI_MOD_SUPER: UiModifiers = 1 << 3;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UiScrollGesture {
#[default]
None,
MouseWheel,
TouchpadPan,
PinchScroll,
}
pub fn classify_scroll(integer_x: i32, integer_y: i32, modifiers: UiModifiers) -> UiScrollGesture {
if modifiers & UI_MOD_CTRL != 0 {
return UiScrollGesture::PinchScroll;
}
if integer_x != 0 || integer_y != 0 {
UiScrollGesture::MouseWheel
} else {
UiScrollGesture::TouchpadPan
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UiPinchPhase {
#[default]
None,
Begin,
Update,
End,
}
#[derive(Clone, Debug, Default)]
pub struct UiEvent {
pub kind: UiEventKind,
pub mouse_x: f32,
pub mouse_y: f32,
pub mouse_dx: f32,
pub mouse_dy: f32,
pub button: i32,
pub click_count: i32,
pub scroll_x: f32,
pub scroll_y: f32,
pub integer_scroll_x: i32,
pub integer_scroll_y: i32,
pub timestamp_ns: u64,
pub scroll_gesture: UiScrollGesture,
pub gesture_scale: f32,
pub pinch_phase: UiPinchPhase,
pub key: UiKey,
pub modifiers: UiModifiers,
pub key_repeat: bool,
pub text: String,
pub text_selection_start: i32,
pub text_selection_length: i32,
pub window_w: i32,
pub window_h: i32,
}
impl UiEvent {
pub fn ctrl(&self) -> bool {
self.modifiers & UI_MOD_CTRL != 0
}
pub fn shift(&self) -> bool {
self.modifiers & UI_MOD_SHIFT != 0
}
pub fn alt(&self) -> bool {
self.modifiers & UI_MOD_ALT != 0
}
pub fn mouse_move(x: f32, y: f32, modifiers: UiModifiers) -> Self {
Self {
kind: UiEventKind::MouseMove,
mouse_x: x,
mouse_y: y,
modifiers,
gesture_scale: 1.0,
text_selection_start: -1,
text_selection_length: -1,
..Default::default()
}
}
pub fn mouse_down(x: f32, y: f32, button: i32, click_count: i32, modifiers: UiModifiers) -> Self {
Self {
kind: UiEventKind::MouseDown,
mouse_x: x,
mouse_y: y,
button,
click_count,
modifiers,
gesture_scale: 1.0,
text_selection_start: -1,
text_selection_length: -1,
..Default::default()
}
}
pub fn mouse_up(x: f32, y: f32, button: i32, modifiers: UiModifiers) -> Self {
Self {
kind: UiEventKind::MouseUp,
mouse_x: x,
mouse_y: y,
button,
modifiers,
gesture_scale: 1.0,
text_selection_start: -1,
text_selection_length: -1,
..Default::default()
}
}
pub fn key_down(key: UiKey, modifiers: UiModifiers, repeat: bool) -> Self {
Self {
kind: UiEventKind::KeyDown,
key,
modifiers,
key_repeat: repeat,
gesture_scale: 1.0,
text_selection_start: -1,
text_selection_length: -1,
..Default::default()
}
}
pub fn key_up(key: UiKey, modifiers: UiModifiers) -> Self {
Self {
kind: UiEventKind::KeyUp,
key,
modifiers,
gesture_scale: 1.0,
text_selection_start: -1,
text_selection_length: -1,
..Default::default()
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UiEventKind {
#[default]
None,
MouseMove,
MouseDown,
MouseUp,
MouseScroll,
KeyDown,
KeyUp,
KeyChar,
WindowResize,
WindowClose,
WindowFocus,
WindowBlur,
Pinch,
TextEditing,
MouseEnter,
MouseLeave,
}
#[derive(Clone, Debug, Default)]
pub struct UiInputState {
pub mouse_x: f32,
pub mouse_y: f32,
pub mouse_dx: f32,
pub mouse_dy: f32,
pub l_button: bool,
pub m_button: bool,
pub r_button: bool,
pub l_pressed: bool,
pub l_released: bool,
pub l_click_count: i32,
pub scroll_x: f32,
pub scroll_y: f32,
pub modifiers: UiModifiers,
pub pointer_down: bool,
pub pointer_x: f32,
pub pointer_y: f32,
pub shift: bool,
pub ctrl: bool,
}
impl UiInputState {
pub const EMPTY: UiInputState = UiInputState {
mouse_x: 0.0,
mouse_y: 0.0,
mouse_dx: 0.0,
mouse_dy: 0.0,
l_button: false,
m_button: false,
r_button: false,
l_pressed: false,
l_released: false,
l_click_count: 0,
scroll_x: 0.0,
scroll_y: 0.0,
modifiers: UI_MOD_NONE,
pointer_down: false,
pointer_x: 0.0,
pointer_y: 0.0,
shift: false,
ctrl: false,
};
}
#[derive(Clone, Debug, Default)]
pub struct UiTabItem {
pub id: String,
pub label: String,
pub dirty: bool,
pub closable: bool,
pub enabled: bool,
}
impl UiTabItem {
pub fn new(id: impl Into<String>, label: impl Into<String>) -> Self {
Self {
id: id.into(),
label: label.into(),
closable: true,
enabled: true,
dirty: false,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct UiTabBarState {
pub selected: i32,
pub first_visible: i32,
}
#[derive(Clone, Debug, Default)]
pub struct UiTabBarResult {
pub bar: PixelRect,
pub tabs: PixelRect,
pub first_visible: i32,
pub one_past_last: i32,
pub activated_index: i32,
pub close_requested_index: i32,
pub selection_changed: bool,
}
#[derive(Clone, Debug, Default)]
pub struct UiTreeRowConfig {
pub depth: i32,
pub indent: i32,
pub disclosure_width: i32,
pub has_children: bool,
pub open: bool,
pub selected: bool,
pub enabled: bool,
}
impl UiTreeRowConfig {
pub const fn new() -> Self {
Self {
depth: 0,
indent: 16,
disclosure_width: 18,
has_children: false,
open: false,
selected: false,
enabled: true,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct UiTreeRowResult {
pub row: PixelRect,
pub disclosure: PixelRect,
pub label: PixelRect,
pub activated: bool,
pub open_changed: bool,
pub open: bool,
}
#[derive(Clone, Debug)]
pub struct NodeCanvasState {
pub pan: [f32; 2],
pub zoom: f32,
pub view_size: [f32; 2],
}
impl Default for NodeCanvasState {
fn default() -> Self {
Self {
pan: [0.0, 0.0],
zoom: 1.0,
view_size: [0.0, 0.0],
}
}
}
#[derive(Clone, Debug, Default)]
pub struct NodeCanvas {
state: NodeCanvasState,
}
impl NodeCanvas {
pub fn new() -> Self {
Self::default()
}
pub fn set_view_size(&mut self, w: f32, h: f32) -> crate::Result<()> {
self.state.view_size = [w, h];
Ok(())
}
pub fn state(&self) -> &NodeCanvasState {
&self.state
}
pub fn world_to_screen(&self, world: [f32; 2]) -> [f32; 2] {
let cx = self.state.view_size[0] * 0.5;
let cy = self.state.view_size[1] * 0.5;
[
cx + (world[0] - self.state.pan[0]) * self.state.zoom,
cy + (world[1] - self.state.pan[1]) * self.state.zoom,
]
}
pub fn screen_to_world(&self, screen: [f32; 2]) -> [f32; 2] {
let cx = self.state.view_size[0] * 0.5;
let cy = self.state.view_size[1] * 0.5;
let inv = if self.state.zoom.abs() > 1e-9 {
1.0 / self.state.zoom
} else {
1.0
};
[
self.state.pan[0] + (screen[0] - cx) * inv,
self.state.pan[1] + (screen[1] - cy) * inv,
]
}
pub fn pan(&mut self, delta: [f32; 2]) {
let inv = if self.state.zoom.abs() > 1e-9 {
1.0 / self.state.zoom
} else {
1.0
};
self.state.pan[0] -= delta[0] * inv;
self.state.pan[1] -= delta[1] * inv;
}
}
#[derive(Clone, Debug)]
pub struct UiScrollConfig {
pub wheel_step: f32,
pub scrollbar_width: f32,
pub scrollbar_gap: f32,
pub show_scrollbar: bool,
}
impl Default for UiScrollConfig {
fn default() -> Self {
Self {
wheel_step: 40.0,
scrollbar_width: 6.0,
scrollbar_gap: 2.0,
show_scrollbar: true,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct UiScrollRegion {
pub viewport: PixelRect,
pub content: PixelRect,
pub offset_y: i32,
pub max_offset_y: i32,
}
#[derive(Clone, Copy, Debug, Default)]
pub struct UiVirtualRange {
pub first: i32,
pub one_past_last: i32,
}
impl UiVirtualRange {
pub fn is_empty(self) -> bool {
self.one_past_last <= self.first
}
}
#[derive(Clone, Debug)]
pub struct UiSplitConfig {
pub direction: UiDirection,
pub handle_size: f32,
pub minimum_first: f32,
pub minimum_second: f32,
pub keyboard_step: f32,
}
impl Default for UiSplitConfig {
fn default() -> Self {
Self {
direction: super::types::UiDirection::Row,
handle_size: 4.0,
minimum_first: 60.0,
minimum_second: 60.0,
keyboard_step: 8.0,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct UiSplitRegion {
pub first: PixelRect,
pub handle: PixelRect,
pub second: PixelRect,
pub changed: bool,
}
#[derive(Clone, Debug)]
pub struct UiPropertyRowConfig {
pub label_fraction: f32,
pub gap: f32,
pub padding_x: f32,
pub alternate: bool,
}
impl Default for UiPropertyRowConfig {
fn default() -> Self {
Self {
label_fraction: 0.40,
gap: 4.0,
padding_x: 4.0,
alternate: false,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct UiPropertyRegion {
pub row: PixelRect,
pub label: PixelRect,
pub value: PixelRect,
}
#[derive(Clone, Debug)]
pub struct UiPopupConfig {
pub width: f32,
pub height: f32,
pub gap: f32,
pub padding: f32,
}
impl Default for UiPopupConfig {
fn default() -> Self {
Self {
width: 160.0,
height: 0.0,
gap: 2.0,
padding: 6.0,
}
}
}
#[derive(Clone, Debug)]
pub struct UiDropdownConfig {
pub max_visible_items: i32,
pub popup_width: f32,
pub popup_gap: f32,
}
impl Default for UiDropdownConfig {
fn default() -> Self {
Self {
max_visible_items: 8,
popup_width: 0.0,
popup_gap: 2.0,
}
}
}
#[derive(Clone, Debug)]
pub struct UiTooltipConfig {
pub delay_ms: f32,
pub max_width: f32,
pub gap: f32,
pub padding: f32,
}
impl Default for UiTooltipConfig {
fn default() -> Self {
Self {
delay_ms: 400.0,
max_width: 240.0,
gap: 8.0,
padding: 6.0,
}
}
}
#[derive(Clone, Debug)]
pub struct UiHeatmapConfig {
pub rows: i32,
pub cols: i32,
pub v_min: f32,
pub v_max: f32,
pub colormap: i32,
pub auto_scale: bool,
pub show_grid: bool,
}
impl Default for UiHeatmapConfig {
fn default() -> Self {
Self {
rows: 1,
cols: 1,
v_min: 0.0,
v_max: 1.0,
colormap: 1,
auto_scale: true,
show_grid: false,
}
}
}
#[derive(Clone, Debug)]
pub struct UiGridConfig {
pub minor_spacing: f32,
pub major_every: i32,
pub super_major_every: i32,
pub minor_thickness: f32,
pub major_thickness: f32,
pub super_major_thickness: f32,
pub axis_thickness: f32,
pub fill_background: bool,
pub draw_axes: bool,
}
impl Default for UiGridConfig {
fn default() -> Self {
Self {
minor_spacing: 20.0,
major_every: 5,
super_major_every: 10,
minor_thickness: 0.5,
major_thickness: 1.0,
super_major_thickness: 1.5,
axis_thickness: 1.5,
fill_background: true,
draw_axes: true,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UiIcon {
#[default]
Previous,
Next,
Play,
Pause,
Menu,
Minimize,
Maximize,
Restore,
Close,
Fullscreen,
ExitFullscreen,
Volume,
Muted,
Settings,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UiIconButtonStyle {
#[default]
Overlay,
Header,
WindowControl,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UiChevronDirection {
#[default]
Previous,
Next,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UiAccessibilityRole {
#[default]
Button,
Checkbox,
Slider,
TextField,
ComboBox,
MenuItem,
Tab,
TreeItem,
Splitter,
Timeline,
}
#[derive(Clone, Debug, Default)]
pub struct UiAccessibilitySnapshot {
pub node_count: usize,
}