1#![deny(missing_docs)]
54use 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)]
99pub enum InputButtonState {
101 None,
103 Pressed(f32),
105 Released,
107 Scroll(f32),
109}
110
111#[derive(Debug, Copy, Clone)]
112pub enum MouseEvent {
114 None,
116 Click(Vec2i),
118 Drag {
120 prev_pos: Vec2i,
122 curr_pos: Vec2i,
124 },
125 Move(Vec2i),
127}
128
129#[derive(Default, Copy, Clone, Eq, PartialEq, Hash, Debug)]
130pub struct Id(usize);
132
133impl Id {
134 pub fn from_ptr<T: ?Sized>(value: &T) -> Self { Self(value as *const T as *const () as usize) }
136
137 pub fn raw(self) -> usize { self.0 }
139}
140
141pub trait Renderer {
143 fn get_atlas(&self) -> AtlasHandle;
145 fn begin(&mut self, width: i32, height: i32, clr: Color);
147 fn push_quad_vertices(&mut self, v0: &Vertex, v1: &Vertex, v2: &Vertex, v3: &Vertex);
149 fn flush(&mut self);
151 fn end(&mut self);
153 fn create_texture(&mut self, id: TextureId, width: i32, height: i32, pixels: &[u8]);
155 fn destroy_texture(&mut self, id: TextureId);
157 fn draw_texture(&mut self, id: TextureId, vertices: [Vertex; 4]);
159}
160
161pub struct RendererHandle<R: Renderer> {
163 handle: Arc<RwLock<R>>,
164}
165
166impl<R: Renderer> Clone for RendererHandle<R> {
168 fn clone(&self) -> Self { Self { handle: self.handle.clone() } }
169}
170
171impl<R: Renderer> RendererHandle<R> {
172 pub fn new(renderer: R) -> Self { Self { handle: Arc::new(RwLock::new(renderer)) } }
174
175 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 f(&*poisoned.into_inner())
183 }
184 }
185 }
186
187 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 f(&mut *poisoned.into_inner())
195 }
196 }
197 }
198}
199
200#[derive(PartialEq, Copy, Clone)]
201#[repr(u32)]
202pub enum Clip {
204 None = 0,
206 Part = 1,
208 All = 2,
210}
211
212#[derive(PartialEq, Copy, Clone)]
213#[repr(u32)]
214pub enum ControlColor {
216 Max = 14,
218 ScrollThumb = 13,
220 ScrollBase = 12,
222 BaseFocus = 11,
224 BaseHover = 10,
226 Base = 9,
228 ButtonFocus = 8,
230 ButtonHover = 7,
232 Button = 6,
234 PanelBG = 5,
236 TitleText = 4,
238 TitleBG = 3,
240 WindowBG = 2,
242 Border = 1,
244 Text = 0,
246}
247
248impl ControlColor {
249 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 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 pub struct ResourceState : u32 {
273 const CHANGE = 4;
275 const SUBMIT = 2;
277 const ACTIVE = 1;
279 const NONE = 0;
281 }
282}
283
284impl ResourceState {
285 pub fn is_changed(&self) -> bool { self.intersects(Self::CHANGE) }
287 pub fn is_submitted(&self) -> bool { self.intersects(Self::SUBMIT) }
289 pub fn is_active(&self) -> bool { self.intersects(Self::ACTIVE) }
291 pub fn is_none(&self) -> bool { self.bits() == 0 }
293}
294
295bitflags! {
296 #[derive(Copy, Clone)]
297 pub struct ContainerOption : u32 {
299 const AUTO_SIZE = 512;
301 const NO_TITLE = 128;
303 const NO_CLOSE = 64;
305 const NO_RESIZE = 16;
307 const NO_FRAME = 8;
309 const NO_INTERACT = 4;
311 const NONE = 0;
313 }
314
315 #[derive(Copy, Clone)]
316 pub struct WidgetOption : u32 {
318 const HOLD_FOCUS = 256;
320 const NO_FRAME = 128;
322 const NO_INTERACT = 4;
324 const ALIGN_RIGHT = 2;
326 const ALIGN_CENTER = 1;
328 const NONE = 0;
330 }
331
332 #[derive(Copy, Clone)]
333 pub struct WidgetFillOption : u32 {
335 const NORMAL = 1;
337 const HOVER = 2;
339 const CLICK = 4;
341 const ALL = Self::NORMAL.bits() | Self::HOVER.bits() | Self::CLICK.bits();
343 }
344}
345
346#[derive(Copy, Clone, Debug, PartialEq, Eq)]
347pub enum WidgetBehaviourOption {
349 None,
351 GrabScroll,
353 NoScroll,
355}
356
357impl WidgetBehaviourOption {
358 pub const NONE: Self = Self::None;
360 pub const GRAB_SCROLL: Self = Self::GrabScroll;
362 pub const NO_SCROLL: Self = Self::NoScroll;
364
365 pub fn is_grab_scroll(self) -> bool { matches!(self, Self::GrabScroll) }
367 pub fn is_no_scroll(self) -> bool { matches!(self, Self::NoScroll) }
369}
370
371#[derive(Copy, Clone, Default, Debug)]
372pub struct ControlState {
375 pub hovered: bool,
377 pub focused: bool,
379 pub clicked: bool,
381 pub active: bool,
383 pub scroll_delta: Option<Vec2i>,
385}
386
387#[derive(Clone, Debug)]
388pub struct InputSnapshot {
390 pub mouse_pos: Vec2i,
392 pub mouse_delta: Vec2i,
394 pub mouse_down: MouseButton,
396 pub mouse_pressed: MouseButton,
398 pub key_mods: KeyMode,
400 pub key_pressed: KeyMode,
402 pub key_codes: KeyCode,
404 pub key_code_pressed: KeyCode,
406 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
426pub trait Widget {
430 fn widget_opt(&self) -> &WidgetOption;
432 fn behaviour_opt(&self) -> &WidgetBehaviourOption;
434 fn get_id(&self) -> Id { Id::from_ptr(self) }
436 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 pub fn is_auto_sizing(&self) -> bool { self.intersects(Self::AUTO_SIZE) }
449
450 pub fn has_no_title(&self) -> bool { self.intersects(Self::NO_TITLE) }
452
453 pub fn has_no_close(&self) -> bool { self.intersects(Self::NO_CLOSE) }
455
456 pub fn is_fixed(&self) -> bool { self.intersects(Self::NO_RESIZE) }
458 pub fn has_no_frame(&self) -> bool { self.intersects(Self::NO_FRAME) }
460}
461
462impl WidgetOption {
463 pub fn is_holding_focus(&self) -> bool { self.intersects(WidgetOption::HOLD_FOCUS) }
465
466 pub fn has_no_frame(&self) -> bool { self.intersects(WidgetOption::NO_FRAME) }
468
469 pub fn is_not_interactive(&self) -> bool { self.intersects(WidgetOption::NO_INTERACT) }
471 pub fn is_aligned_right(&self) -> bool { self.intersects(WidgetOption::ALIGN_RIGHT) }
473 pub fn is_aligned_center(&self) -> bool { self.intersects(WidgetOption::ALIGN_CENTER) }
475 pub fn is_none(&self) -> bool { self.bits() == 0 }
477}
478
479impl WidgetFillOption {
480 pub fn fill_normal(&self) -> bool { self.intersects(Self::NORMAL) }
482
483 pub fn fill_hover(&self) -> bool { self.intersects(Self::HOVER) }
485
486 pub fn fill_click(&self) -> bool { self.intersects(Self::CLICK) }
488}
489
490bitflags! {
491 #[derive(Copy, Clone, Debug)]
492 pub struct MouseButton : u32 {
494 const MIDDLE = 4;
496 const RIGHT = 2;
498 const LEFT = 1;
500 const NONE = 0;
502 }
503}
504
505impl MouseButton {
506 pub fn is_middle(&self) -> bool { self.intersects(Self::MIDDLE) }
508 pub fn is_right(&self) -> bool { self.intersects(Self::RIGHT) }
510 pub fn is_left(&self) -> bool { self.intersects(Self::LEFT) }
512 pub fn is_none(&self) -> bool { self.bits() == 0 }
514}
515
516bitflags! {
517 #[derive(Copy, Clone, Debug)]
518 pub struct KeyMode : u32 {
520 const RETURN = 16;
522 const BACKSPACE = 8;
524 const ALT = 4;
526 const CTRL = 2;
528 const SHIFT = 1;
530 const NONE = 0;
532 }
533}
534
535impl KeyMode {
536 pub fn is_none(&self) -> bool { self.bits() == 0 }
538 pub fn is_return(&self) -> bool { self.intersects(Self::RETURN) }
540 pub fn is_backspace(&self) -> bool { self.intersects(Self::BACKSPACE) }
542 pub fn is_alt(&self) -> bool { self.intersects(Self::ALT) }
544 pub fn is_ctrl(&self) -> bool { self.intersects(Self::CTRL) }
546 pub fn is_shift(&self) -> bool { self.intersects(Self::SHIFT) }
548}
549
550bitflags! {
551 #[derive(Copy, Clone, Debug)]
552 pub struct KeyCode : u32 {
554 const RIGHT = 8;
556 const LEFT = 4;
558 const DOWN = 2;
560 const UP = 1;
562 const NONE = 0;
564 }
565}
566
567impl KeyCode {
568 pub fn is_none(&self) -> bool { self.bits() == 0 }
570 pub fn is_up(&self) -> bool { self.intersects(Self::UP) }
572 pub fn is_down(&self) -> bool { self.intersects(Self::DOWN) }
574 pub fn is_left(&self) -> bool { self.intersects(Self::LEFT) }
576 pub fn is_right(&self) -> bool { self.intersects(Self::RIGHT) }
578}
579
580#[derive(Clone, Debug)]
581pub 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 pub fn rel_mouse_pos(&self) -> Vec2i { self.rel_mouse_pos }
619
620 pub fn key_state(&self) -> KeyMode { self.key_down }
622
623 pub fn key_codes(&self) -> KeyCode { self.key_code_down }
625
626 pub fn text_input(&self) -> &str { &self.input_text }
628
629 pub fn mousemove(&mut self, x: i32, y: i32) { self.mouse_pos = vec2(x, y); }
631
632 pub fn get_mouse_buttons(&self) -> MouseButton { self.mouse_down }
634
635 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 pub fn mouseup(&mut self, x: i32, y: i32, btn: MouseButton) {
644 self.mousemove(x, y);
645 self.mouse_down &= !btn;
646 }
647
648 pub fn scroll(&mut self, x: i32, y: i32) {
650 self.scroll_delta.x += x;
651 self.scroll_delta.y += y;
652 }
653
654 pub fn keydown(&mut self, key: KeyMode) {
656 self.key_pressed |= key;
657 self.key_down |= key;
658 }
659
660 pub fn keyup(&mut self, key: KeyMode) { self.key_down &= !key; }
662
663 pub fn keydown_code(&mut self, code: KeyCode) {
665 self.key_code_pressed |= code;
666 self.key_code_down |= code;
667 }
668
669 pub fn keyup_code(&mut self, code: KeyCode) { self.key_code_down &= !code; }
671
672 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)]
694pub struct Color {
696 pub r: u8,
698 pub g: u8,
700 pub b: u8,
702 pub a: u8,
704}
705
706pub trait Font {
708 fn name(&self) -> &str;
710 fn get_size(&self) -> usize;
712 fn get_char_size(&self, c: char) -> (usize, usize);
714}
715
716#[derive(Copy, Clone)]
717pub struct Style {
719 pub font: FontId,
721 pub default_cell_width: i32,
723 pub padding: i32,
725 pub spacing: i32,
727 pub indent: i32,
729 pub title_height: i32,
731 pub scrollbar_size: i32,
733 pub thumb_size: i32,
735 pub colors: [Color; 14],
737}
738
739pub type Real = f32;
741
742#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
743pub struct TextureId(u32);
745
746impl TextureId {
747 pub fn raw(self) -> u32 { self.0 }
749}
750
751#[derive(Copy, Clone, Debug, PartialEq, Eq)]
752pub enum Image {
754 Slot(SlotId),
756 Texture(TextureId),
758}
759
760#[derive(Copy, Clone)]
762pub enum ImageSource<'a> {
763 Raw {
765 width: i32,
767 height: i32,
769 pixels: &'a [u8],
771 },
772 #[cfg(any(feature = "builder", feature = "png_source"))]
773 Png {
776 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
819pub fn vec2(x: i32, y: i32) -> Vec2i { Vec2i { x, y } }
821
822pub fn rect(x: i32, y: i32, w: i32, h: i32) -> Recti { Recti { x, y, width: w, height: h } }
824
825pub fn color(r: u8, g: u8, b: u8, a: u8) -> Color { Color { r, g, b, a } }
827
828pub 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)]
832pub 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 pub fn inner<'a>(&'a self) -> Ref<'a, Container> { self.0.borrow() }
842
843 pub fn inner_mut<'a>(&'a mut self) -> RefMut<'a, Container> { self.0.borrow_mut() }
845}
846
847pub 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 pub input: Rc<RefCell<Input>>,
862}
863
864impl<R: Renderer> Context<R> {
865 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 pub fn begin(&mut self, width: i32, height: i32, clr: Color) { self.canvas.begin(width, height, clr); }
886
887 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 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 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 self.root_list.sort_by(|a, b| a.zindex().cmp(&b.zindex()));
938 }
939
940 pub fn frame<F: FnOnce(&mut Self)>(&mut self, f: F) {
943 self.frame_begin();
944
945 f(self);
947
948 self.frame_end();
949 }
950
951 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 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 pub fn new_popup(&mut self, name: &str) -> WindowHandle { WindowHandle::popup(name, self.canvas.get_atlas(), self.style.clone(), self.input.clone()) }
965
966 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 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 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 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 if !window.is_open() {
1048 window.inner_mut().main.reset();
1049 }
1050 }
1051 }
1052
1053 pub fn open_dialog(&mut self, window: &mut WindowHandle) { window.inner_mut().win_state = WindowState::Open; }
1055
1056 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 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 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 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 pub fn set_style(&mut self, style: &Style) { self.style = Rc::new(style.clone()) }
1137
1138 pub fn canvas(&self) -> &Canvas<R> { &self.canvas }
1140
1141 pub fn load_image_rgba(&mut self, width: i32, height: i32, pixels: &[u8]) -> TextureId { self.canvas.load_texture_rgba(width, height, pixels) }
1143
1144 pub fn free_image(&mut self, id: TextureId) { self.canvas.free_texture(id); }
1146
1147 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}