microui_redux/
lib.rs

1//
2// Copyright 2022-Present (c) Raja Lehtihet & Wael El Oraiby
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are met:
6//
7// 1. Redistributions of source code must retain the above copyright notice,
8// this list of conditions and the following disclaimer.
9//
10// 2. Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13//
14// 3. Neither the name of the copyright holder nor the names of its contributors
15// may be used to endorse or promote products derived from this software without
16// specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28// POSSIBILITY OF SUCH DAMAGE.
29//
30// -----------------------------------------------------------------------------
31// Ported to rust from https://github.com/rxi/microui/ and the original license
32//
33// Copyright (c) 2020 rxi
34//
35// Permission is hereby granted, free of charge, to any person obtaining a copy
36// of this software and associated documentation files (the "Software"), to
37// deal in the Software without restriction, including without limitation the
38// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
39// sell copies of the Software, and to permit persons to whom the Software is
40// furnished to do so, subject to the following conditions:
41//
42// The above copyright notice and this permission notice shall be included in
43// all copies or substantial portions of the Software.
44//
45// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
46// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
47// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
48// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
49// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
50// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
51// IN THE SOFTWARE.
52//
53#![deny(missing_docs)]
54//! `microui-redux` provides an immediate-mode GUI toolkit inspired by [rxi/microui](https://github.com/rxi/microui).
55//! The crate exposes the core context, container, layout, and renderer hooks necessary to embed a UI inside
56//! custom render backends while remaining allocator- and platform-agnostic.
57
58use std::{
59    cell::{Ref, RefCell, RefMut},
60    f32,
61    hash::Hash,
62    rc::Rc,
63    sync::Arc,
64};
65
66#[cfg(any(feature = "builder", feature = "png_source"))]
67use std::io::Cursor;
68
69#[cfg(any(feature = "builder", feature = "png_source"))]
70use png::{ColorType, Decoder};
71
72mod atlas;
73mod canvas;
74mod draw_context;
75mod container;
76mod file_dialog;
77mod layout;
78mod rect_packer;
79mod window;
80mod widgets;
81
82pub use atlas::*;
83pub use canvas::*;
84pub use container::*;
85pub use layout::SizePolicy;
86pub use rect_packer::*;
87pub use rs_math3d::*;
88pub use window::*;
89pub use file_dialog::*;
90pub use widgets::*;
91
92use layout::LayoutManager;
93
94use bitflags::*;
95use std::cmp::{max, min};
96use std::sync::RwLock;
97
98#[derive(Debug, Copy, Clone)]
99/// Tracks input button transitions seen since the previous frame.
100pub enum InputButtonState {
101    /// No interaction was registered.
102    None,
103    /// The button was pressed this frame, storing the press timestamp.
104    Pressed(f32),
105    /// The button was released this frame.
106    Released,
107    /// The scroll wheel moved by the given amount.
108    Scroll(f32),
109}
110
111#[derive(Debug, Copy, Clone)]
112/// Records the latest pointer interaction that occurred over a widget.
113pub enum MouseEvent {
114    /// No pointer activity occurred.
115    None,
116    /// The pointer clicked at the given pixel position.
117    Click(Vec2i),
118    /// The pointer is being dragged between two positions.
119    Drag {
120        /// Position where the drag originated.
121        prev_pos: Vec2i,
122        /// Current drag position.
123        curr_pos: Vec2i,
124    },
125    /// The pointer moved to a new coordinate without interacting.
126    Move(Vec2i),
127}
128
129#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
130/// Numeric identifier assigned to widgets and containers.
131pub struct Id(usize);
132
133impl Id {
134    /// Creates an ID from the address of a stable state object.
135    pub fn from_ptr<T: ?Sized>(value: &T) -> Self { Self(value as *const T as *const () as usize) }
136
137    /// Returns the raw pointer value wrapped by this ID.
138    pub fn raw(self) -> usize { self.0 }
139}
140
141/// Trait implemented by render backends used by the UI context.
142pub trait Renderer {
143    /// Returns the atlas backing the renderer.
144    fn get_atlas(&self) -> AtlasHandle;
145    /// Begins a new frame with the viewport size and clear color.
146    fn begin(&mut self, width: i32, height: i32, clr: Color);
147    /// Pushes four vertices representing a quad to the backend.
148    fn push_quad_vertices(&mut self, v0: &Vertex, v1: &Vertex, v2: &Vertex, v3: &Vertex);
149    /// Flushes any buffered geometry to the GPU.
150    fn flush(&mut self);
151    /// Ends the frame, finalizing any outstanding GPU work.
152    fn end(&mut self);
153    /// Creates a texture owned by the renderer.
154    fn create_texture(&mut self, id: TextureId, width: i32, height: i32, pixels: &[u8]);
155    /// Destroys a previously created texture.
156    fn destroy_texture(&mut self, id: TextureId);
157    /// Draws the provided textured quad.
158    fn draw_texture(&mut self, id: TextureId, vertices: [Vertex; 4]);
159}
160
161/// Thread-safe handle that shares ownership of a [`Renderer`].
162pub struct RendererHandle<R: Renderer> {
163    handle: Arc<RwLock<R>>,
164}
165
166// seems there's a bug in #[derive(Clone)] as it's unable to induce that Arc is sufficient
167impl<R: Renderer> Clone for RendererHandle<R> {
168    fn clone(&self) -> Self { Self { handle: self.handle.clone() } }
169}
170
171impl<R: Renderer> RendererHandle<R> {
172    /// Wraps a renderer inside an [`Arc<RwLock<...>>`] so it can be shared.
173    pub fn new(renderer: R) -> Self { Self { handle: Arc::new(RwLock::new(renderer)) } }
174
175    /// Executes the provided closure with a shared reference to the renderer.
176    pub fn scope<Res, F: Fn(&R) -> Res>(&self, f: F) -> Res {
177        match self.handle.read() {
178            Ok(guard) => f(&*guard),
179            Err(poisoned) => {
180                // Handle poisoned lock by using the data anyway
181                // This is safe because we're just reading
182                f(&*poisoned.into_inner())
183            }
184        }
185    }
186
187    /// Executes the provided closure with a mutable reference to the renderer.
188    pub fn scope_mut<Res, F: FnMut(&mut R) -> Res>(&mut self, mut f: F) -> Res {
189        match self.handle.write() {
190            Ok(mut guard) => f(&mut *guard),
191            Err(poisoned) => {
192                // Handle poisoned lock by using the data anyway
193                // Clear the poison and continue
194                f(&mut *poisoned.into_inner())
195            }
196        }
197    }
198}
199
200#[derive(PartialEq, Copy, Clone)]
201#[repr(u32)]
202/// Describes whether a rectangle is clipped by the current scissor.
203pub enum Clip {
204    /// Rectangle is fully visible.
205    None = 0,
206    /// Rectangle is partially visible.
207    Part = 1,
208    /// Rectangle is fully clipped away.
209    All = 2,
210}
211
212#[derive(PartialEq, Copy, Clone)]
213#[repr(u32)]
214/// Identifiers for each of the built-in style colors.
215pub enum ControlColor {
216    /// Number of color entries in [`Style::colors`].
217    Max = 14,
218    /// Thumb of scrollbars.
219    ScrollThumb = 13,
220    /// Base frame of scrollbars.
221    ScrollBase = 12,
222    /// Base color for focused widgets.
223    BaseFocus = 11,
224    /// Base color while the pointer hovers the widget.
225    BaseHover = 10,
226    /// Default base color.
227    Base = 9,
228    /// Button color while the widget is focused.
229    ButtonFocus = 8,
230    /// Button color while the pointer hovers the widget.
231    ButtonHover = 7,
232    /// Default button color.
233    Button = 6,
234    /// Panel background color.
235    PanelBG = 5,
236    /// Window title text color.
237    TitleText = 4,
238    /// Window title background color.
239    TitleBG = 3,
240    /// Window background color.
241    WindowBG = 2,
242    /// Outline/border color.
243    Border = 1,
244    /// Default text color.
245    Text = 0,
246}
247
248impl ControlColor {
249    /// Promotes the enum to the hover variant when relevant.
250    pub fn hover(&mut self) {
251        *self = match self {
252            Self::Base => Self::BaseHover,
253            Self::Button => Self::ButtonHover,
254            _ => *self,
255        }
256    }
257
258    /// Promotes the enum to the focused variant when relevant.
259    pub fn focus(&mut self) {
260        *self = match self {
261            Self::Base => Self::BaseFocus,
262            Self::Button => Self::ButtonFocus,
263            Self::BaseHover => Self::BaseFocus,
264            Self::ButtonHover => Self::ButtonFocus,
265            _ => *self,
266        }
267    }
268}
269
270bitflags! {
271    /// State bits returned by widgets to describe their interaction outcome.
272    pub struct ResourceState : u32 {
273        /// Indicates that the widget's data changed.
274        const CHANGE = 4;
275        /// Indicates that the widget was submitted (e.g. button clicked).
276        const SUBMIT = 2;
277        /// Indicates that the widget is currently active.
278        const ACTIVE = 1;
279        /// Indicates no interaction.
280        const NONE = 0;
281    }
282}
283
284impl ResourceState {
285    /// Returns `true` if the widget changed its bound value.
286    pub fn is_changed(&self) -> bool { self.intersects(Self::CHANGE) }
287    /// Returns `true` if the widget signaled submission.
288    pub fn is_submitted(&self) -> bool { self.intersects(Self::SUBMIT) }
289    /// Returns `true` if the widget is active.
290    pub fn is_active(&self) -> bool { self.intersects(Self::ACTIVE) }
291    /// Returns `true` if the state contains no flags.
292    pub fn is_none(&self) -> bool { self.bits() == 0 }
293}
294
295bitflags! {
296        #[derive(Copy, Clone)]
297    /// Options that control how a container behaves.
298    pub struct ContainerOption : u32 {
299        /// Automatically adapts the container size to its content.
300        const AUTO_SIZE = 512;
301        /// Hides the title bar.
302        const NO_TITLE = 128;
303        /// Hides the close button.
304        const NO_CLOSE = 64;
305        /// Prevents the user from resizing the window.
306        const NO_RESIZE = 16;
307        /// Hides the outer frame.
308        const NO_FRAME = 8;
309        /// Reserved for future use (currently unused by the container).
310        const NO_INTERACT = 4;
311        /// No special options.
312        const NONE = 0;
313    }
314
315    #[derive(Copy, Clone)]
316    /// Widget specific options that influence layout and interactivity.
317    pub struct WidgetOption : u32 {
318        /// Keeps keyboard focus while the widget is held.
319        const HOLD_FOCUS = 256;
320        /// Draws the widget without its frame/background.
321        const NO_FRAME = 128;
322        /// Disables interaction for the widget.
323        const NO_INTERACT = 4;
324        /// Aligns the widget to the right side of the cell.
325        const ALIGN_RIGHT = 2;
326        /// Centers the widget inside the cell.
327        const ALIGN_CENTER = 1;
328        /// No special options.
329        const NONE = 0;
330    }
331
332    #[derive(Copy, Clone)]
333    /// Controls which widget states should draw a filled background.
334    pub struct WidgetFillOption : u32 {
335        /// Fill the background for the idle/normal state.
336        const NORMAL = 1;
337        /// Fill the background while hovered.
338        const HOVER = 2;
339        /// Fill the background while actively clicked.
340        const CLICK = 4;
341        /// Fill the background for every interaction state.
342        const ALL = Self::NORMAL.bits() | Self::HOVER.bits() | Self::CLICK.bits();
343    }
344}
345
346#[derive(Copy, Clone, Debug, PartialEq, Eq)]
347/// Behaviour options that control how widgets and containers handle input side effects.
348pub enum WidgetBehaviourOption {
349    /// No special behaviour.
350    None,
351    /// Consume pending scroll when the widget is hovered.
352    GrabScroll,
353    /// Disable container scroll handling.
354    NoScroll,
355}
356
357impl WidgetBehaviourOption {
358    /// No special behaviour.
359    pub const NONE: Self = Self::None;
360    /// Consume pending scroll when the widget is hovered.
361    pub const GRAB_SCROLL: Self = Self::GrabScroll;
362    /// Disable container scroll handling.
363    pub const NO_SCROLL: Self = Self::NoScroll;
364
365    /// Returns `true` if the option enables scroll grabbing for a widget.
366    pub fn is_grab_scroll(self) -> bool { matches!(self, Self::GrabScroll) }
367    /// Returns `true` if the option disables container scroll handling.
368    pub fn is_no_scroll(self) -> bool { matches!(self, Self::NoScroll) }
369}
370
371#[derive(Copy, Clone, Default, Debug)]
372/// Captures the interaction state for a widget during the current frame.
373/// Produced by `Container::update_control` and passed into `Widget::handle`.
374pub struct ControlState {
375    /// Cursor is hovering the widget.
376    pub hovered: bool,
377    /// Widget currently owns focus.
378    pub focused: bool,
379    /// Mouse was pressed on the widget this frame.
380    pub clicked: bool,
381    /// Mouse is held down while the widget is focused.
382    pub active: bool,
383    /// Scroll delta consumed by this widget, if any.
384    pub scroll_delta: Option<Vec2i>,
385}
386
387#[derive(Clone, Debug)]
388/// Snapshot of the per-frame input state for widgets that need it.
389pub struct InputSnapshot {
390    /// Absolute mouse position in screen coordinates.
391    pub mouse_pos: Vec2i,
392    /// Mouse movement delta since the previous frame.
393    pub mouse_delta: Vec2i,
394    /// Currently held mouse buttons.
395    pub mouse_down: MouseButton,
396    /// Mouse buttons pressed this frame.
397    pub mouse_pressed: MouseButton,
398    /// Active modifier keys.
399    pub key_mods: KeyMode,
400    /// Modifier keys pressed this frame.
401    pub key_pressed: KeyMode,
402    /// Active navigation keys.
403    pub key_codes: KeyCode,
404    /// Navigation keys pressed this frame.
405    pub key_code_pressed: KeyCode,
406    /// UTF-8 text input collected this frame.
407    pub text_input: String,
408}
409
410impl Default for InputSnapshot {
411    fn default() -> Self {
412        Self {
413            mouse_pos: Vec2i::default(),
414            mouse_delta: Vec2i::default(),
415            mouse_down: MouseButton::NONE,
416            mouse_pressed: MouseButton::NONE,
417            key_mods: KeyMode::NONE,
418            key_pressed: KeyMode::NONE,
419            key_codes: KeyCode::NONE,
420            key_code_pressed: KeyCode::NONE,
421            text_input: String::new(),
422        }
423    }
424}
425
426/// Trait implemented by persistent widget state structures.
427/// `handle` is invoked with a `WidgetCtx` and precomputed `ControlState`.
428/// The default ID is derived from the state address, so the state must live at a stable address.
429pub trait Widget {
430    /// Returns the widget options for this state.
431    fn widget_opt(&self) -> &WidgetOption;
432    /// Returns the behaviour options for this state.
433    fn behaviour_opt(&self) -> &WidgetBehaviourOption;
434    /// Returns the widget identifier for this state.
435    fn get_id(&self) -> Id { Id::from_ptr(self) }
436    /// Handles widget interaction and rendering for the current frame using the provided context.
437    fn handle(&mut self, ctx: &mut WidgetCtx<'_>, control: &ControlState) -> ResourceState;
438}
439
440impl Widget for (WidgetOption, WidgetBehaviourOption) {
441    fn widget_opt(&self) -> &WidgetOption { &self.0 }
442    fn behaviour_opt(&self) -> &WidgetBehaviourOption { &self.1 }
443    fn handle(&mut self, _ctx: &mut WidgetCtx<'_>, _control: &ControlState) -> ResourceState { ResourceState::NONE }
444}
445
446impl ContainerOption {
447    /// Returns `true` if the option requests automatic sizing.
448    pub fn is_auto_sizing(&self) -> bool { self.intersects(Self::AUTO_SIZE) }
449
450    /// Returns `true` if the title bar should be hidden.
451    pub fn has_no_title(&self) -> bool { self.intersects(Self::NO_TITLE) }
452
453    /// Returns `true` if the close button should be hidden.
454    pub fn has_no_close(&self) -> bool { self.intersects(Self::NO_CLOSE) }
455
456    /// Returns `true` if the container is fixed-size.
457    pub fn is_fixed(&self) -> bool { self.intersects(Self::NO_RESIZE) }
458    /// Returns `true` if the outer frame is hidden.
459    pub fn has_no_frame(&self) -> bool { self.intersects(Self::NO_FRAME) }
460}
461
462impl WidgetOption {
463    /// Returns `true` if the widget should keep focus while held.
464    pub fn is_holding_focus(&self) -> bool { self.intersects(WidgetOption::HOLD_FOCUS) }
465
466    /// Returns `true` if the widget shouldn't draw its frame.
467    pub fn has_no_frame(&self) -> bool { self.intersects(WidgetOption::NO_FRAME) }
468
469    /// Returns `true` if the widget is non-interactive.
470    pub fn is_not_interactive(&self) -> bool { self.intersects(WidgetOption::NO_INTERACT) }
471    /// Returns `true` if the widget prefers right alignment.
472    pub fn is_aligned_right(&self) -> bool { self.intersects(WidgetOption::ALIGN_RIGHT) }
473    /// Returns `true` if the widget prefers centered alignment.
474    pub fn is_aligned_center(&self) -> bool { self.intersects(WidgetOption::ALIGN_CENTER) }
475    /// Returns `true` if the option set is empty.
476    pub fn is_none(&self) -> bool { self.bits() == 0 }
477}
478
479impl WidgetFillOption {
480    /// Returns `true` when the normal state should be filled.
481    pub fn fill_normal(&self) -> bool { self.intersects(Self::NORMAL) }
482
483    /// Returns `true` when the hover state should be filled.
484    pub fn fill_hover(&self) -> bool { self.intersects(Self::HOVER) }
485
486    /// Returns `true` when the clicked/active state should be filled.
487    pub fn fill_click(&self) -> bool { self.intersects(Self::CLICK) }
488}
489
490bitflags! {
491    #[derive(Copy, Clone, Debug)]
492    /// Mouse button state as reported by the input system.
493    pub struct MouseButton : u32 {
494        /// Middle mouse button.
495        const MIDDLE = 4;
496        /// Right mouse button.
497        const RIGHT = 2;
498        /// Left mouse button.
499        const LEFT = 1;
500        /// No buttons pressed.
501        const NONE = 0;
502    }
503}
504
505impl MouseButton {
506    /// Returns `true` if the middle mouse button is pressed.
507    pub fn is_middle(&self) -> bool { self.intersects(Self::MIDDLE) }
508    /// Returns `true` if the right mouse button is pressed.
509    pub fn is_right(&self) -> bool { self.intersects(Self::RIGHT) }
510    /// Returns `true` if the left mouse button is pressed.
511    pub fn is_left(&self) -> bool { self.intersects(Self::LEFT) }
512    /// Returns `true` if no mouse buttons are pressed.
513    pub fn is_none(&self) -> bool { self.bits() == 0 }
514}
515
516bitflags! {
517    #[derive(Copy, Clone, Debug)]
518    /// Modifier key state tracked by the input system.
519    pub struct KeyMode : u32 {
520        /// Return/Enter key held.
521        const RETURN = 16;
522        /// Backspace key held.
523        const BACKSPACE = 8;
524        /// Alt key held.
525        const ALT = 4;
526        /// Control key held.
527        const CTRL = 2;
528        /// Shift key held.
529        const SHIFT = 1;
530        /// No modifiers active.
531        const NONE = 0;
532    }
533}
534
535impl KeyMode {
536    /// Returns `true` if no modifiers are active.
537    pub fn is_none(&self) -> bool { self.bits() == 0 }
538    /// Returns `true` if Return/Enter is held.
539    pub fn is_return(&self) -> bool { self.intersects(Self::RETURN) }
540    /// Returns `true` if Backspace is held.
541    pub fn is_backspace(&self) -> bool { self.intersects(Self::BACKSPACE) }
542    /// Returns `true` if Alt is held.
543    pub fn is_alt(&self) -> bool { self.intersects(Self::ALT) }
544    /// Returns `true` if Control is held.
545    pub fn is_ctrl(&self) -> bool { self.intersects(Self::CTRL) }
546    /// Returns `true` if Shift is held.
547    pub fn is_shift(&self) -> bool { self.intersects(Self::SHIFT) }
548}
549
550bitflags! {
551    #[derive(Copy, Clone, Debug)]
552    /// Logical navigation keys handled by the UI.
553    pub struct KeyCode : u32 {
554        /// Right arrow key.
555        const RIGHT = 8;
556        /// Left arrow key.
557        const LEFT = 4;
558        /// Down arrow key.
559        const DOWN = 2;
560        /// Up arrow key.
561        const UP = 1;
562        /// No navigation keys pressed.
563        const NONE = 0;
564    }
565}
566
567impl KeyCode {
568    /// Returns `true` if no navigation key is pressed.
569    pub fn is_none(&self) -> bool { self.bits() == 0 }
570    /// Returns `true` if up is pressed.
571    pub fn is_up(&self) -> bool { self.intersects(Self::UP) }
572    /// Returns `true` if down is pressed.
573    pub fn is_down(&self) -> bool { self.intersects(Self::DOWN) }
574    /// Returns `true` if left is pressed.
575    pub fn is_left(&self) -> bool { self.intersects(Self::LEFT) }
576    /// Returns `true` if right is pressed.
577    pub fn is_right(&self) -> bool { self.intersects(Self::RIGHT) }
578}
579
580#[derive(Clone, Debug)]
581/// Aggregates raw input collected during the current frame.
582pub struct Input {
583    mouse_pos: Vec2i,
584    last_mouse_pos: Vec2i,
585    mouse_delta: Vec2i,
586    scroll_delta: Vec2i,
587    rel_mouse_pos: Vec2i,
588    mouse_down: MouseButton,
589    mouse_pressed: MouseButton,
590    key_down: KeyMode,
591    key_pressed: KeyMode,
592    key_code_down: KeyCode,
593    key_code_pressed: KeyCode,
594    input_text: String,
595}
596
597impl Default for Input {
598    fn default() -> Self {
599        Self {
600            mouse_pos: Vec2i::default(),
601            last_mouse_pos: Vec2i::default(),
602            mouse_delta: Vec2i::default(),
603            rel_mouse_pos: Vec2i::default(),
604            scroll_delta: Vec2i::default(),
605            mouse_down: MouseButton::NONE,
606            mouse_pressed: MouseButton::NONE,
607            key_down: KeyMode::NONE,
608            key_pressed: KeyMode::NONE,
609            key_code_down: KeyCode::NONE,
610            key_code_pressed: KeyCode::NONE,
611            input_text: String::default(),
612        }
613    }
614}
615
616impl Input {
617    /// Returns the mouse position relative to the container that currently owns focus.
618    pub fn rel_mouse_pos(&self) -> Vec2i { self.rel_mouse_pos }
619
620    /// Returns the state of all modifier keys.
621    pub fn key_state(&self) -> KeyMode { self.key_down }
622
623    /// Returns the state of all navigation keys.
624    pub fn key_codes(&self) -> KeyCode { self.key_code_down }
625
626    /// Returns the accumulated UTF-8 text entered this frame.
627    pub fn text_input(&self) -> &str { &self.input_text }
628
629    /// Updates the current mouse pointer position.
630    pub fn mousemove(&mut self, x: i32, y: i32) { self.mouse_pos = vec2(x, y); }
631
632    /// Returns the currently held mouse buttons.
633    pub fn get_mouse_buttons(&self) -> MouseButton { self.mouse_down }
634
635    /// Records that the specified mouse button was pressed.
636    pub fn mousedown(&mut self, x: i32, y: i32, btn: MouseButton) {
637        self.mousemove(x, y);
638        self.mouse_down |= btn;
639        self.mouse_pressed |= btn;
640    }
641
642    /// Records that the specified mouse button was released.
643    pub fn mouseup(&mut self, x: i32, y: i32, btn: MouseButton) {
644        self.mousemove(x, y);
645        self.mouse_down &= !btn;
646    }
647
648    /// Accumulates scroll wheel movement.
649    pub fn scroll(&mut self, x: i32, y: i32) {
650        self.scroll_delta.x += x;
651        self.scroll_delta.y += y;
652    }
653
654    /// Records that a modifier key was pressed.
655    pub fn keydown(&mut self, key: KeyMode) {
656        self.key_pressed |= key;
657        self.key_down |= key;
658    }
659
660    /// Records that a modifier key was released.
661    pub fn keyup(&mut self, key: KeyMode) { self.key_down &= !key; }
662
663    /// Records that a navigation key was pressed.
664    pub fn keydown_code(&mut self, code: KeyCode) {
665        self.key_code_pressed |= code;
666        self.key_code_down |= code;
667    }
668
669    /// Records that a navigation key was released.
670    pub fn keyup_code(&mut self, code: KeyCode) { self.key_code_down &= !code; }
671
672    /// Appends UTF-8 text to the input buffer.
673    pub fn text(&mut self, text: &str) {
674        self.input_text.push_str(text);
675    }
676
677    fn prelude(&mut self) {
678        self.mouse_delta.x = self.mouse_pos.x - self.last_mouse_pos.x;
679        self.mouse_delta.y = self.mouse_pos.y - self.last_mouse_pos.y;
680    }
681
682    fn epilogue(&mut self) {
683        self.key_pressed = KeyMode::NONE;
684        self.key_code_pressed = KeyCode::NONE;
685        self.input_text.clear();
686        self.mouse_pressed = MouseButton::NONE;
687        self.scroll_delta = vec2(0, 0);
688        self.last_mouse_pos = self.mouse_pos;
689    }
690}
691
692#[derive(Default, Copy, Clone)]
693#[repr(C)]
694/// Simple RGBA color stored with 8-bit components.
695pub struct Color {
696    /// Red channel.
697    pub r: u8,
698    /// Green channel.
699    pub g: u8,
700    /// Blue channel.
701    pub b: u8,
702    /// Alpha channel.
703    pub a: u8,
704}
705
706/// Describes the interface the atlas uses to query font metadata.
707pub trait Font {
708    /// Returns the font's display name.
709    fn name(&self) -> &str;
710    /// Returns the base pixel size of the font.
711    fn get_size(&self) -> usize;
712    /// Returns the pixel width and height for a specific character.
713    fn get_char_size(&self, c: char) -> (usize, usize);
714}
715
716#[derive(Copy, Clone)]
717/// Collection of visual constants that drive widget appearance.
718pub struct Style {
719    /// Font used for all text rendering.
720    pub font: FontId,
721    /// Default width used by layouts when no size policy overrides it.
722    pub default_cell_width: i32,
723    /// Inner padding applied to most widgets.
724    pub padding: i32,
725    /// Spacing between cells in a layout.
726    pub spacing: i32,
727    /// Indentation applied to nested content.
728    pub indent: i32,
729    /// Height of window title bars.
730    pub title_height: i32,
731    /// Width of scrollbars.
732    pub scrollbar_size: i32,
733    /// Size of slider thumbs.
734    pub thumb_size: i32,
735    /// Palette of [`ControlColor`] entries.
736    pub colors: [Color; 14],
737}
738
739/// Floating-point type used by widgets and layout calculations.
740pub type Real = f32;
741
742#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
743/// Handle referencing a renderer-owned texture.
744pub struct TextureId(u32);
745
746impl TextureId {
747    /// Returns the raw numeric identifier stored inside the handle.
748    pub fn raw(self) -> u32 { self.0 }
749}
750
751#[derive(Copy, Clone, Debug, PartialEq, Eq)]
752/// Either a slot stored inside the atlas or a standalone texture.
753pub enum Image {
754    /// Reference to an atlas slot.
755    Slot(SlotId),
756    /// Reference to an external texture ID.
757    Texture(TextureId),
758}
759
760/// Describes image bytes that can be uploaded to a texture.
761#[derive(Copy, Clone)]
762pub enum ImageSource<'a> {
763    /// Raw RGBA pixels laid out as width × height × 4 bytes.
764    Raw {
765        /// Width in pixels.
766        width: i32,
767        /// Height in pixels.
768        height: i32,
769        /// Pixel buffer in RGBA8888 format.
770        pixels: &'a [u8],
771    },
772    #[cfg(any(feature = "builder", feature = "png_source"))]
773    /// PNG-compressed byte slice (requires the `builder` or `png_source` feature).
774    /// Grayscale and RGB images are expanded to opaque RGBA (alpha = 255).
775    Png {
776        /// Compressed PNG payload.
777        bytes: &'a [u8],
778    },
779}
780
781static UNCLIPPED_RECT: Recti = Recti {
782    x: 0,
783    y: 0,
784    width: i32::MAX,
785    height: i32::MAX,
786};
787
788impl Default for Style {
789    fn default() -> Self {
790        Self {
791            font: FontId::default(),
792            default_cell_width: 68,
793            padding: 5,
794            spacing: 4,
795            indent: 24,
796            title_height: 24,
797            scrollbar_size: 12,
798            thumb_size: 8,
799            colors: [
800                Color { r: 230, g: 230, b: 230, a: 255 },
801                Color { r: 25, g: 25, b: 25, a: 255 },
802                Color { r: 50, g: 50, b: 50, a: 255 },
803                Color { r: 25, g: 25, b: 25, a: 255 },
804                Color { r: 240, g: 240, b: 240, a: 255 },
805                Color { r: 0, g: 0, b: 0, a: 0 },
806                Color { r: 75, g: 75, b: 75, a: 255 },
807                Color { r: 95, g: 95, b: 95, a: 255 },
808                Color { r: 115, g: 115, b: 115, a: 255 },
809                Color { r: 30, g: 30, b: 30, a: 255 },
810                Color { r: 35, g: 35, b: 35, a: 255 },
811                Color { r: 40, g: 40, b: 40, a: 255 },
812                Color { r: 43, g: 43, b: 43, a: 255 },
813                Color { r: 30, g: 30, b: 30, a: 255 },
814            ],
815        }
816    }
817}
818
819/// Convenience constructor for [`Vec2i`].
820pub fn vec2(x: i32, y: i32) -> Vec2i { Vec2i { x, y } }
821
822/// Convenience constructor for [`Recti`].
823pub fn rect(x: i32, y: i32, w: i32, h: i32) -> Recti { Recti { x, y, width: w, height: h } }
824
825/// Convenience constructor for [`Color`].
826pub fn color(r: u8, g: u8, b: u8, a: u8) -> Color { Color { r, g, b, a } }
827
828/// Expands (or shrinks) a rectangle uniformly on all sides.
829pub fn expand_rect(r: Recti, n: i32) -> Recti { rect(r.x - n, r.y - n, r.width + n * 2, r.height + n * 2) }
830
831#[derive(Clone)]
832/// Shared handle to a container that can be embedded inside windows or panels.
833pub struct ContainerHandle(Rc<RefCell<Container>>);
834
835impl ContainerHandle {
836    pub(crate) fn new(container: Container) -> Self { Self(Rc::new(RefCell::new(container))) }
837
838    pub(crate) fn render<R: Renderer>(&mut self, canvas: &mut Canvas<R>) { self.0.borrow_mut().render(canvas) }
839
840    /// Returns an immutable borrow of the underlying container.
841    pub fn inner<'a>(&'a self) -> Ref<'a, Container> { self.0.borrow() }
842
843    /// Returns a mutable borrow of the underlying container.
844    pub fn inner_mut<'a>(&'a mut self) -> RefMut<'a, Container> { self.0.borrow_mut() }
845}
846
847/// Primary entry point used to drive the UI over a renderer implementation.
848pub struct Context<R: Renderer> {
849    canvas: Canvas<R>,
850    style: Rc<Style>,
851
852    last_zindex: i32,
853    frame: usize,
854    hover_root: Option<WindowHandle>,
855    next_hover_root: Option<WindowHandle>,
856    scroll_target: Option<WindowHandle>,
857
858    root_list: Vec<WindowHandle>,
859
860    /// Shared pointer to the input state driving this context.
861    pub input: Rc<RefCell<Input>>,
862}
863
864impl<R: Renderer> Context<R> {
865    /// Creates a new UI context around the provided renderer and dimensions.
866    pub fn new(renderer: RendererHandle<R>, dim: Dimensioni) -> Self {
867        Self {
868            canvas: Canvas::from(renderer, dim),
869            style: Rc::new(Style::default()),
870            last_zindex: 0,
871            frame: 0,
872            hover_root: None,
873            next_hover_root: None,
874            scroll_target: None,
875
876            root_list: Vec::default(),
877
878            input: Rc::new(RefCell::new(Input::default())),
879        }
880    }
881}
882
883impl<R: Renderer> Context<R> {
884    /// Begins a new draw pass on the underlying canvas.
885    pub fn begin(&mut self, width: i32, height: i32, clr: Color) { self.canvas.begin(width, height, clr); }
886
887    /// Flushes recorded draw commands to the renderer and ends the draw pass.
888    pub fn end(&mut self) {
889        for r in &mut self.root_list {
890            r.render(&mut self.canvas);
891        }
892        self.canvas.end()
893    }
894
895    /// Returns a handle to the underlying renderer.
896    pub fn renderer_handle(&self) -> RendererHandle<R> { self.canvas.renderer_handle() }
897
898    #[inline(never)]
899    fn frame_begin(&mut self) {
900        self.scroll_target = None;
901        self.input.borrow_mut().prelude();
902        for r in &mut self.root_list {
903            r.prepare();
904        }
905        self.frame += 1;
906        self.root_list.clear();
907    }
908
909    #[inline(never)]
910    fn frame_end(&mut self) {
911        for r in &mut self.root_list {
912            r.finish();
913        }
914
915        let mouse_pressed = self.input.borrow().mouse_pressed;
916        match (mouse_pressed.is_none(), &self.next_hover_root) {
917            (false, Some(next_hover_root)) if next_hover_root.zindex() < self.last_zindex && next_hover_root.zindex() >= 0 => {
918                self.bring_to_front(&mut next_hover_root.clone());
919            }
920            _ => (),
921        }
922
923        self.input.borrow_mut().epilogue();
924
925        // prepare the next frame
926        self.hover_root = self.next_hover_root.clone();
927        self.next_hover_root = None;
928        for r in &mut self.root_list {
929            r.inner_mut().main.in_hover_root = false;
930        }
931        match &mut self.hover_root {
932            Some(window) => window.inner_mut().main.in_hover_root = true,
933            _ => (),
934        }
935
936        // sort all windows
937        self.root_list.sort_by(|a, b| a.zindex().cmp(&b.zindex()));
938    }
939
940    /// Runs the UI for a single frame by wrapping input/layout bookkeeping.
941    /// Rendering still requires calling [`Context::begin`] and [`Context::end`].
942    pub fn frame<F: FnOnce(&mut Self)>(&mut self, f: F) {
943        self.frame_begin();
944
945        // execute the frame function
946        f(self);
947
948        self.frame_end();
949    }
950
951    /// Creates a new movable window rooted at the provided rectangle.
952    pub fn new_window(&mut self, name: &str, initial_rect: Recti) -> WindowHandle {
953        let mut window = WindowHandle::window(name, self.canvas.get_atlas(), self.style.clone(), self.input.clone(), initial_rect);
954        self.bring_to_front(&mut window);
955        window
956    }
957
958    /// Creates a modal dialog window.
959    pub fn new_dialog(&mut self, name: &str, initial_rect: Recti) -> WindowHandle {
960        WindowHandle::dialog(name, self.canvas.get_atlas(), self.style.clone(), self.input.clone(), initial_rect)
961    }
962
963    /// Creates a popup window that appears under the mouse cursor.
964    pub fn new_popup(&mut self, name: &str) -> WindowHandle { WindowHandle::popup(name, self.canvas.get_atlas(), self.style.clone(), self.input.clone()) }
965
966    /// Creates a standalone panel that can be embedded inside other windows.
967    pub fn new_panel(&mut self, name: &str) -> ContainerHandle {
968        ContainerHandle::new(Container::new(name, self.canvas.get_atlas(), self.style.clone(), self.input.clone()))
969    }
970
971    /// Bumps the window's Z order so it renders above others.
972    pub fn bring_to_front(&mut self, window: &mut WindowHandle) {
973        self.last_zindex += 1;
974        window.inner_mut().main.zindex = self.last_zindex;
975    }
976
977    #[inline(never)]
978    fn begin_root_container(&mut self, window: &mut WindowHandle) {
979        self.root_list.push(window.clone());
980
981        if window.inner().main.rect.contains(&self.input.borrow().mouse_pos)
982            && (self.next_hover_root.is_none() || window.zindex() > self.next_hover_root.as_ref().unwrap().zindex())
983        {
984            self.next_hover_root = Some(window.clone());
985        }
986        let container = &mut window.inner_mut().main;
987        let scroll_delta = self.input.borrow().scroll_delta;
988        let pending_scroll = if container.in_hover_root && (scroll_delta.x != 0 || scroll_delta.y != 0) {
989            Some(scroll_delta)
990        } else {
991            None
992        };
993        container.seed_pending_scroll(pending_scroll);
994        container.clip_stack.push(UNCLIPPED_RECT);
995    }
996
997    #[inline(never)]
998    fn end_root_container(&mut self, window: &mut WindowHandle) {
999        let container = &mut window.inner_mut().main;
1000        container.pop_clip_rect();
1001
1002        let layout_body = container.layout.current_body();
1003        match container.layout.current_max() {
1004            None => (),
1005            Some(lm) => container.content_size = Vec2i::new(lm.x - layout_body.x, lm.y - layout_body.y),
1006        }
1007        container.consume_pending_scroll();
1008        container.layout.pop_scope();
1009    }
1010
1011    #[inline(never)]
1012    #[must_use]
1013    fn begin_window(&mut self, window: &mut WindowHandle, opt: ContainerOption, bopt: WidgetBehaviourOption) -> bool {
1014        if !window.is_open() {
1015            return false;
1016        }
1017
1018        self.begin_root_container(window);
1019        window.begin_window(opt, bopt);
1020
1021        true
1022    }
1023
1024    fn end_window(&mut self, window: &mut WindowHandle) {
1025        window.end_window();
1026        self.end_root_container(window);
1027    }
1028
1029    /// Opens a window, executes the provided UI builder, and closes the window.
1030    pub fn window<F: FnOnce(&mut Container) -> WindowState>(
1031        &mut self,
1032        window: &mut WindowHandle,
1033        opt: ContainerOption,
1034        bopt: WidgetBehaviourOption,
1035        f: F,
1036    ) {
1037        // call the window function if the window is open
1038        if self.begin_window(window, opt, bopt) {
1039            window.inner_mut().main.style = self.style.clone();
1040            let state = f(&mut window.inner_mut().main);
1041            self.end_window(window);
1042            if window.is_open() {
1043                window.inner_mut().win_state = state;
1044            }
1045
1046            // in case the window needs to be reopened, reset all states
1047            if !window.is_open() {
1048                window.inner_mut().main.reset();
1049            }
1050        }
1051    }
1052
1053    /// Marks a dialog window as open for the next frame.
1054    pub fn open_dialog(&mut self, window: &mut WindowHandle) { window.inner_mut().win_state = WindowState::Open; }
1055
1056    /// Renders a dialog window if it is currently open.
1057    pub fn dialog<F: FnOnce(&mut Container) -> WindowState>(
1058        &mut self,
1059        window: &mut WindowHandle,
1060        opt: ContainerOption,
1061        bopt: WidgetBehaviourOption,
1062        f: F,
1063    ) {
1064        if window.is_open() {
1065            self.next_hover_root = Some(window.clone());
1066            self.hover_root = self.next_hover_root.clone();
1067            window.inner_mut().main.in_hover_root = true;
1068            self.bring_to_front(window);
1069
1070            self.window(window, opt, bopt, f);
1071        }
1072    }
1073
1074    /// Shows a popup at the mouse cursor position.
1075    pub fn open_popup(&mut self, window: &mut WindowHandle) {
1076        let was_open = window.is_open();
1077        let mouse_pos = self.input.borrow().mouse_pos;
1078        {
1079            let mut inner = window.inner_mut();
1080            if was_open {
1081                let mut rect = inner.main.rect;
1082                rect.x = mouse_pos.x;
1083                rect.y = mouse_pos.y;
1084                inner.main.rect = rect;
1085            } else {
1086                inner.main.rect = rect(mouse_pos.x, mouse_pos.y, 1, 1);
1087                inner.win_state = WindowState::Open;
1088                inner.main.in_hover_root = true;
1089                inner.main.popup_just_opened = true;
1090            }
1091        }
1092        if !was_open {
1093            self.next_hover_root = Some(window.clone());
1094            self.hover_root = self.next_hover_root.clone();
1095            self.bring_to_front(window);
1096        }
1097    }
1098
1099    /// Shows a popup anchored at the provided rectangle instead of the mouse cursor.
1100    pub fn open_popup_at(&mut self, window: &mut WindowHandle, anchor: Recti) {
1101        let was_open = window.is_open();
1102        {
1103            let mut inner = window.inner_mut();
1104            if was_open {
1105                let mut rect = inner.main.rect;
1106                rect.x = anchor.x;
1107                rect.y = anchor.y;
1108                rect.width = anchor.width;
1109                inner.main.rect = rect;
1110            } else {
1111                inner.main.rect = anchor;
1112                inner.win_state = WindowState::Open;
1113                inner.main.in_hover_root = true;
1114                inner.main.popup_just_opened = true;
1115            }
1116        }
1117        if !was_open {
1118            self.next_hover_root = Some(window.clone());
1119            self.hover_root = self.next_hover_root.clone();
1120            self.bring_to_front(window);
1121        }
1122    }
1123
1124    /// Opens a popup window with default options.
1125    pub fn popup<F: FnOnce(&mut Container) -> WindowState>(
1126        &mut self,
1127        window: &mut WindowHandle,
1128        bopt: WidgetBehaviourOption,
1129        f: F,
1130    ) {
1131        let opt = ContainerOption::AUTO_SIZE | ContainerOption::NO_RESIZE | ContainerOption::NO_TITLE;
1132        self.window(window, opt, bopt, f);
1133    }
1134
1135    /// Replaces the current UI style.
1136    pub fn set_style(&mut self, style: &Style) { self.style = Rc::new(style.clone()) }
1137
1138    /// Returns the underlying canvas used for rendering.
1139    pub fn canvas(&self) -> &Canvas<R> { &self.canvas }
1140
1141    /// Uploads an RGBA image to the renderer and returns its [`TextureId`].
1142    pub fn load_image_rgba(&mut self, width: i32, height: i32, pixels: &[u8]) -> TextureId { self.canvas.load_texture_rgba(width, height, pixels) }
1143
1144    /// Deletes a previously uploaded texture.
1145    pub fn free_image(&mut self, id: TextureId) { self.canvas.free_texture(id); }
1146
1147    /// Uploads texture data described by `source`. PNG decoding is only available when the
1148    /// `png_source` (or `builder`) feature is enabled.
1149    pub fn load_image_from(&mut self, source: ImageSource) -> Result<TextureId, String> {
1150        match source {
1151            ImageSource::Raw { width, height, pixels } => {
1152                Self::assert_rgba_len(width, height, pixels.len())?;
1153                Ok(self.load_image_rgba(width, height, pixels))
1154            }
1155            #[cfg(any(feature = "builder", feature = "png_source"))]
1156            ImageSource::Png { bytes } => {
1157                let (width, height, rgba) = Self::decode_png(bytes)?;
1158                Ok(self.load_image_rgba(width, height, rgba.as_slice()))
1159            }
1160        }
1161    }
1162
1163    fn assert_rgba_len(width: i32, height: i32, len: usize) -> Result<(), String> {
1164        if width <= 0 || height <= 0 {
1165            return Err(String::from("Image dimensions must be positive"));
1166        }
1167        let expected = width as usize * height as usize * 4;
1168        if len != expected {
1169            return Err(format!("Expected {} RGBA bytes, received {}", expected, len));
1170        }
1171        Ok(())
1172    }
1173
1174    #[cfg(any(feature = "builder", feature = "png_source"))]
1175    fn decode_png(bytes: &[u8]) -> Result<(i32, i32, Vec<u8>), String> {
1176        let cursor = Cursor::new(bytes);
1177        let decoder = Decoder::new(cursor);
1178        let mut reader = decoder.read_info().map_err(|e| e.to_string())?;
1179        let buf_size = reader
1180            .output_buffer_size()
1181            .ok_or_else(|| "PNG decoder did not report output size".to_string())?;
1182        let mut buf = vec![0; buf_size];
1183        let info = reader.next_frame(&mut buf).map_err(|e| e.to_string())?;
1184        let raw = &buf[..info.buffer_size()];
1185        let mut rgba = Vec::with_capacity((info.width as usize) * (info.height as usize) * 4);
1186        match info.color_type {
1187            ColorType::Rgba => rgba.extend_from_slice(raw),
1188            ColorType::Rgb => {
1189                for chunk in raw.chunks(3) {
1190                    rgba.extend_from_slice(chunk);
1191                    rgba.push(0xFF);
1192                }
1193            }
1194            ColorType::Grayscale => {
1195                for &v in raw {
1196                    rgba.extend_from_slice(&[v, v, v, 0xFF]);
1197                }
1198            }
1199            ColorType::GrayscaleAlpha => {
1200                for chunk in raw.chunks(2) {
1201                    let v = chunk[0];
1202                    let a = chunk[1];
1203                    rgba.extend_from_slice(&[v, v, v, a]);
1204                }
1205            }
1206            _ => {
1207                return Err("Unsupported PNG color type".into());
1208            }
1209        }
1210        Ok((info.width as i32, info.height as i32, rgba))
1211    }
1212}