Skip to main content

macroquad/
ui.rs

1//! Immediate mode UI.
2//!
3//! Spiritual successor of megaui library, but fully skinnable and configurable.
4//!
5//! The UI entrypoint is `root_ui()` call.
6//! ```ignore
7//! root_ui().label(None, "hello megaui");
8//! if root_ui().button(None, "Push me") {
9//!    println!("pushed");
10//! }
11//! ```
12//! This will draw a label and a button one after each other right on top of the
13//! screen.
14
15pub mod canvas;
16mod clipboard;
17#[macro_use]
18mod hash;
19mod input_handler;
20mod render;
21mod style;
22
23pub mod widgets;
24
25pub use clipboard::ClipboardObject;
26pub use input_handler::{InputHandler, KeyCode};
27pub use render::{DrawList, Vertex};
28pub use style::{Skin, Style, StyleBuilder};
29
30pub use crate::hash;
31
32pub(crate) use render::ElementState;
33
34use std::{
35    borrow::Cow,
36    ops::DerefMut,
37    sync::{Arc, Mutex},
38};
39
40/// Root UI. Widgets drawn with the root ui will be always presented at the end of the frame with a "default" camera.
41/// UI space would be a "default" screen space (0..screen_width(), 0..screen_height())
42pub fn root_ui() -> impl DerefMut<Target = Ui> {
43    crate::get_context().ui_context.ui.borrow_mut()
44}
45
46/// Current camera world space UI.
47/// Widgets will be drawn either at the end of the frame or just before next "set_camera" clal
48/// UI space would be equal to the camera space, widgets will be drawn at the plane with Y up X right and Z = 0.
49/// Note that windows focus queue, input focus etc is shared across all cameras.
50/// So this:
51///
52/// ```skip
53/// camera_ui().draw_window();
54/// set_camera(..);
55/// camera_ui().draw_window();
56/// root_ui().draw_window();
57/// ```
58/// Will result 3 windows on the screen, all in different cameras and probably looking differently,
59/// but only one of them would be focused.
60#[doc(hidden)]
61#[allow(unreachable_code)]
62pub fn camera_ui() -> impl DerefMut<Target = Ui> {
63    unimplemented!() as &'static mut Ui
64}
65
66use crate::{
67    math::{Rect, RectOffset, Vec2},
68    text::{
69        atlas::{Atlas, SpriteKey},
70        Font,
71    },
72    texture::Image,
73    ui::{canvas::DrawCanvas, render::Painter},
74};
75
76use std::collections::HashMap;
77mod cursor;
78mod input;
79mod key_repeat;
80
81use cursor::Cursor;
82use input::Input;
83
84pub use cursor::Layout;
85use input::{InputCharacter, Key};
86
87/// Is used to keep track of internal state of various widgets like [widgets::Window](macroquad::ui::widgets::Window)
88/// These should be unique per window and ideally not change in between frames.
89pub type Id = u64;
90
91pub enum UiContent<'a> {
92    Label(Cow<'a, str>),
93    Texture(crate::texture::Texture2D),
94}
95
96impl<'a> From<&'a str> for UiContent<'a> {
97    fn from(data: &'a str) -> UiContent<'a> {
98        UiContent::Label(data.into())
99    }
100}
101
102impl From<String> for UiContent<'static> {
103    fn from(data: String) -> UiContent<'static> {
104        UiContent::Label(data.into())
105    }
106}
107
108impl From<crate::texture::Texture2D> for UiContent<'static> {
109    fn from(data: crate::texture::Texture2D) -> UiContent<'static> {
110        UiContent::Texture(data)
111    }
112}
113
114pub(crate) struct Window {
115    pub id: Id,
116    pub parent: Option<Id>,
117    // active is set to true when the begin_window is called on this window
118    // and is going to be set to false at the end of each frame
119    pub active: bool,
120    // was the window "active" during the last frame
121    // the way to find out which windows should be rendered after end of the frame and during next frame, before begin_window of the next frame will be called on each window
122    pub was_active: bool,
123    pub title_height: f32,
124    pub position: Vec2,
125    pub size: Vec2,
126    pub vertical_scroll_bar_width: f32,
127    pub movable: bool,
128    pub painter: Painter,
129    pub cursor: Cursor,
130    pub childs: Vec<Id>,
131    pub want_close: bool,
132    pub force_focus: bool,
133
134    margin: f32,
135    window_margin: RectOffset,
136}
137
138impl Window {
139    pub fn new(
140        id: Id,
141        parent: Option<Id>,
142        position: Vec2,
143        size: Vec2,
144        title_height: f32,
145        window_margin: RectOffset,
146        margin: f32,
147        movable: bool,
148        force_focus: bool,
149        atlas: Arc<Mutex<Atlas>>,
150    ) -> Window {
151        Window {
152            id,
153            position,
154            size,
155            vertical_scroll_bar_width: 0.,
156            title_height,
157            parent,
158            was_active: false,
159            active: false,
160            painter: Painter::new(atlas),
161            cursor: Cursor::new(
162                Rect::new(
163                    position.x + window_margin.left,
164                    position.y + title_height + window_margin.top,
165                    size.x - window_margin.left - window_margin.right,
166                    size.y - title_height - window_margin.top - window_margin.bottom,
167                ),
168                margin,
169            ),
170            margin,
171            window_margin,
172            childs: vec![],
173            want_close: false,
174            movable,
175            force_focus,
176        }
177    }
178
179    pub fn resize(&mut self, size: Vec2) {
180        self.size = size;
181        self.cursor = Cursor::new(
182            Rect::new(
183                self.position.x + self.window_margin.left,
184                self.position.y + self.title_height + self.window_margin.top,
185                self.size.x - self.window_margin.left - self.window_margin.right,
186                self.size.y
187                    - self.title_height
188                    - self.window_margin.top
189                    - self.window_margin.bottom,
190            ),
191            self.margin,
192        );
193    }
194
195    pub const fn top_level(&self) -> bool {
196        self.parent.is_none()
197    }
198
199    pub const fn full_rect(&self) -> Rect {
200        Rect::new(self.position.x, self.position.y, self.size.x, self.size.y)
201    }
202
203    pub fn content_rect(&self) -> Rect {
204        Rect::new(
205            self.position.x,
206            self.position.y + self.title_height,
207            self.size.x - self.vertical_scroll_bar_width,
208            self.size.y - self.title_height,
209        )
210    }
211
212    pub fn set_position(&mut self, position: Vec2) {
213        self.position = position;
214        self.cursor.area.x = position.x + self.window_margin.left;
215        self.cursor.area.y = position.y + self.title_height + self.window_margin.top;
216    }
217
218    pub const fn title_rect(&self) -> Rect {
219        Rect::new(
220            self.position.x,
221            self.position.y,
222            self.size.x,
223            self.title_height,
224        )
225    }
226
227    pub fn same_line(&mut self, x: f32) {
228        self.cursor.next_same_line = Some(x);
229    }
230}
231
232#[derive(Copy, Clone, Debug)]
233pub enum DragState {
234    Clicked(Vec2),
235    Dragging(Vec2),
236}
237
238#[derive(Copy, Clone, Debug)]
239pub enum Drag {
240    No,
241    Dragging(Vec2, Option<Id>),
242    Dropped(Vec2, Option<Id>),
243}
244
245struct StyleStack {
246    default_skin: Skin,
247    custom_skin_stack: Vec<Skin>,
248}
249
250impl StyleStack {
251    fn new(atlas: Arc<Mutex<Atlas>>, default_font: Arc<Mutex<Font>>) -> StyleStack {
252        StyleStack {
253            default_skin: Skin::new(atlas, default_font),
254            custom_skin_stack: vec![],
255        }
256    }
257
258    fn top(&self) -> &Skin {
259        self.custom_skin_stack.last().unwrap_or(&self.default_skin)
260    }
261}
262
263pub(crate) struct TabSelector {
264    counter: isize,
265    wants: Option<isize>,
266    to_change: Option<isize>,
267}
268
269impl TabSelector {
270    const fn new() -> Self {
271        TabSelector {
272            counter: 0,
273            wants: None,
274            to_change: None,
275        }
276    }
277
278    fn new_frame(&mut self) {
279        self.to_change = if self.wants == Some(-1) {
280            Some(self.counter - 1)
281        } else if self.wants == Some(self.counter) {
282            Some(0)
283        } else {
284            self.wants
285        };
286        self.wants = None;
287        self.counter = 0;
288    }
289
290    /// Returns true if this widget should gain focus, because user pressed `Tab` or `Shift + Tab`.
291    pub(crate) fn register_selectable_widget(&mut self, has_focus: bool, input: &Input) -> bool {
292        if has_focus {
293            enum PressedTabKey {
294                Tab,
295                ShiftTab,
296                Other,
297            }
298
299            let key = if input
300                .input_buffer
301                .iter()
302                .any(|inp| inp.key == Key::KeyCode(KeyCode::Tab) && inp.modifier_shift)
303            {
304                PressedTabKey::ShiftTab
305            } else if input
306                .input_buffer
307                .iter()
308                .any(|inp| inp.key == Key::KeyCode(KeyCode::Tab))
309            {
310                PressedTabKey::Tab
311            } else {
312                PressedTabKey::Other
313            };
314
315            match key {
316                PressedTabKey::Tab => self.wants = Some(self.counter + 1),
317                PressedTabKey::ShiftTab => self.wants = Some(self.counter - 1),
318                PressedTabKey::Other => {}
319            }
320        }
321
322        let result = if self.to_change.map(|id| id == self.counter).unwrap_or(false) {
323            self.to_change = None;
324            true
325        } else {
326            false
327        };
328
329        self.counter += 1;
330
331        result
332    }
333}
334
335pub struct Ui {
336    input: Input,
337    skin_stack: StyleStack,
338    /// Returns the number of frames that have elapsed since the program started.
339    pub frame: u64,
340    pub(crate) time: f32,
341
342    moving: Option<(Id, Vec2)>,
343    windows: HashMap<Id, Window>,
344    // special window that is always rendered on top of anything
345    // TODO: maybe make modal windows stack instead
346    modal: Option<Window>,
347    // another special window
348    // always rendered behind everything and do not have borders or scrolls
349    // helps using window-less uis
350    root_window: Window,
351    windows_focus_order: Vec<Id>,
352
353    storage_u32: HashMap<Id, u32>,
354    storage_any: AnyStorage,
355
356    dragging: Option<(Id, DragState)>,
357    drag_hovered: Option<Id>,
358    drag_hovered_previous_frame: Option<Id>,
359    active_window: Option<Id>,
360    hovered_window: Id,
361    in_modal: bool,
362    child_window_stack: Vec<Id>,
363
364    last_item_clicked: bool,
365    last_item_hovered: bool,
366
367    pub(crate) atlas: Arc<Mutex<Atlas>>,
368    pub(crate) default_font: Arc<Mutex<Font>>,
369
370    clipboard_selection: String,
371    clipboard: Box<dyn crate::ui::ClipboardObject>,
372
373    key_repeat: key_repeat::KeyRepeat,
374
375    tab_selector: TabSelector,
376    input_focus: Option<Id>,
377}
378
379#[derive(Default)]
380pub(crate) struct AnyStorage {
381    storage: HashMap<Id, Box<dyn std::any::Any>>,
382}
383
384impl AnyStorage {
385    pub(crate) fn get_or_insert_with<T: Default + 'static, F: Fn() -> T>(
386        &mut self,
387        id: Id,
388        f: F,
389    ) -> &mut T {
390        self.storage
391            .entry(id)
392            .or_insert_with(|| Box::new(f()))
393            .downcast_mut::<T>()
394            .unwrap()
395    }
396
397    pub(crate) fn get_or_default<T: Default + 'static>(&mut self, id: Id) -> &mut T {
398        self.storage
399            .entry(id)
400            .or_insert_with(|| Box::new(T::default()))
401            .downcast_mut::<T>()
402            .unwrap()
403    }
404}
405
406pub(crate) struct WindowContext<'a> {
407    pub window: &'a mut Window,
408    pub dragging: &'a mut Option<(Id, DragState)>,
409    pub drag_hovered: &'a mut Option<Id>,
410    pub drag_hovered_previous_frame: &'a mut Option<Id>,
411    pub storage_u32: &'a mut HashMap<Id, u32>,
412    pub storage_any: &'a mut AnyStorage,
413    pub style: &'a Skin,
414    pub input: &'a mut Input,
415    pub clipboard_selection: &'a mut String,
416    pub clipboard: &'a mut dyn crate::ui::ClipboardObject,
417    pub focused: bool,
418    pub last_item_clicked: &'a mut bool,
419    pub last_item_hovered: &'a mut bool,
420    pub tab_selector: &'a mut TabSelector,
421    pub input_focus: &'a mut Option<Id>,
422}
423
424impl<'a> WindowContext<'a> {
425    pub(crate) fn scroll_area(&mut self) {
426        let inner_rect = self.window.cursor.scroll.inner_rect_previous_frame;
427        let rect = self.window.content_rect();
428        let rect = Rect {
429            w: rect.w + self.window.vertical_scroll_bar_width,
430            ..rect
431        };
432
433        self.window.cursor.scroll.scroll = Vec2::new(
434            -self.window.cursor.scroll.rect.x,
435            -self.window.cursor.scroll.rect.y,
436        );
437
438        if inner_rect.h > rect.h {
439            self.window.vertical_scroll_bar_width = self.style.scroll_width;
440            self.draw_vertical_scroll_bar(
441                rect,
442                Rect::new(
443                    rect.x + rect.w - self.style.scroll_width,
444                    rect.y,
445                    self.style.scroll_width,
446                    rect.h,
447                ),
448            );
449        } else {
450            self.window.vertical_scroll_bar_width = 0.;
451        }
452
453        self.window.cursor.scroll.update();
454    }
455
456    pub(crate) fn close(&mut self) {
457        self.window.want_close = true;
458    }
459
460    fn draw_vertical_scroll_bar(&mut self, area: Rect, rect: Rect) {
461        let scroll = &mut self.window.cursor.scroll;
462        let inner_rect = scroll.inner_rect_previous_frame;
463        let size = scroll.rect.h / inner_rect.h * rect.h;
464        let pos = (scroll.rect.y - inner_rect.y) / inner_rect.h * rect.h;
465
466        self.window.painter.draw_line(
467            Vec2::new(rect.x, rect.y),
468            Vec2::new(rect.x, rect.y + rect.h),
469            self.style.scrollbar_style.color(ElementState {
470                focused: self.focused,
471                ..Default::default()
472            }),
473        );
474
475        let mut clicked = false;
476        let mut hovered = false;
477        let bar = Rect::new(rect.x + 1., rect.y + pos, rect.w - 1., size);
478        let k = inner_rect.h / scroll.rect.h;
479        if bar.contains(self.input.mouse_position) {
480            hovered = true;
481        }
482        if hovered && self.input.is_mouse_down() {
483            self.input.cursor_grabbed = true;
484            scroll.dragging_y = true;
485            scroll.initial_scroll.y = scroll.rect.y - self.input.mouse_position.y * k;
486        }
487        if scroll.dragging_y && self.input.is_mouse_down == false {
488            self.input.cursor_grabbed = false;
489            scroll.dragging_y = false;
490        }
491        if scroll.dragging_y {
492            clicked = true;
493            scroll.scroll_to(self.input.mouse_position.y * k + scroll.initial_scroll.y);
494        }
495
496        if self.focused
497            && area.contains(self.input.mouse_position)
498            && self.input.mouse_wheel.y != 0.
499        {
500            scroll.scroll_to(
501                scroll.rect.y + self.input.mouse_wheel.y * k * self.style.scroll_multiplier,
502            );
503        }
504
505        self.window.painter.draw_rect(
506            bar,
507            None,
508            self.style.scrollbar_handle_style.color(ElementState {
509                focused: self.focused,
510                hovered,
511                clicked,
512                selected: false,
513            }),
514        );
515    }
516
517    pub fn register_click_intention(&mut self, rect: Rect) -> (bool, bool) {
518        *self.last_item_hovered =
519            self.input.window_active && rect.contains(self.input.mouse_position);
520        *self.last_item_clicked = *self.last_item_hovered && self.input.click_down();
521
522        (*self.last_item_hovered, *self.last_item_clicked)
523    }
524
525    pub fn input_focused(&self, id: Id) -> bool {
526        self.input_focus
527            .map_or(false, |input_focus| input_focus == id)
528    }
529}
530
531impl InputHandler for Ui {
532    fn mouse_down(&mut self, position: (f32, f32)) {
533        let position = Vec2::new(position.0, position.1);
534
535        self.input.is_mouse_down = true;
536        self.input.click_down = true;
537        self.input.mouse_position = position;
538
539        if let Some(ref window) = self.modal {
540            let rect = Rect::new(
541                window.position.x,
542                window.position.y,
543                window.size.x,
544                window.size.y,
545            );
546            if window.was_active && rect.contains(position) {
547                return;
548            }
549        }
550
551        for (n, window) in self.windows_focus_order.iter().enumerate() {
552            let window = &self.windows[window];
553
554            if window.was_active == false {
555                continue;
556            }
557
558            if window.top_level() && window.title_rect().contains(position) && window.movable {
559                self.moving = Some((
560                    window.id,
561                    position - Vec2::new(window.position.x, window.position.y),
562                ));
563            }
564
565            if window.top_level() && window.full_rect().contains(position) {
566                let window = self.windows_focus_order.remove(n);
567                self.windows_focus_order.insert(0, window);
568                return;
569            }
570        }
571    }
572
573    fn mouse_up(&mut self, _: (f32, f32)) {
574        self.input.is_mouse_down = false;
575        self.input.click_up = true;
576        self.moving = None;
577    }
578
579    fn mouse_wheel(&mut self, x: f32, y: f32) {
580        self.input.mouse_wheel = Vec2::new(x, y);
581    }
582
583    fn mouse_move(&mut self, position: (f32, f32)) {
584        let position = Vec2::new(position.0, position.1);
585
586        // assuming that the click was to the root window
587        // if it is not - hovered_window will be set a little later in that function
588        self.hovered_window = 0;
589        for window in self.windows_focus_order.iter() {
590            let window = &self.windows[window];
591
592            if window.top_level() && window.was_active && window.full_rect().contains(position) {
593                self.hovered_window = window.id;
594                break;
595            }
596        }
597
598        match &self.modal {
599            Some(modal) if modal.was_active || modal.active => {
600                if modal.full_rect().contains(position) {
601                    self.hovered_window = modal.id;
602                }
603            }
604            _ => {}
605        }
606
607        self.input.mouse_position = position;
608        if let Some((id, orig)) = self.moving.as_ref() {
609            self.windows
610                .get_mut(id)
611                .unwrap()
612                .set_position(Vec2::new(position.x - orig.x, position.y - orig.y));
613        }
614    }
615
616    fn char_event(&mut self, character: char, shift: bool, ctrl: bool) {
617        self.input.modifier_ctrl = ctrl;
618        self.input.input_buffer.push(input::InputCharacter {
619            key: input::Key::Char(character),
620            modifier_shift: shift,
621            modifier_ctrl: ctrl,
622        });
623    }
624
625    fn key_down(&mut self, key: KeyCode, shift: bool, ctrl: bool) {
626        self.input.modifier_ctrl = ctrl;
627
628        if key == KeyCode::Escape {
629            self.input.escape = true;
630        }
631        if key == KeyCode::Enter {
632            self.input.enter = true;
633        }
634
635        if ctrl && (key == KeyCode::C || key == KeyCode::X) {
636            self.clipboard.set(&self.clipboard_selection);
637        }
638
639        if key != KeyCode::Control && self.key_repeat.add_repeat_gap(key, self.time) {
640            self.input.input_buffer.push(input::InputCharacter {
641                key: input::Key::KeyCode(key),
642                modifier_shift: shift,
643                modifier_ctrl: ctrl,
644            });
645        }
646    }
647}
648
649impl Ui {
650    pub fn new(
651        ctx: &mut dyn miniquad::RenderingBackend,
652        screen_width: f32,
653        screen_height: f32,
654    ) -> Ui {
655        let atlas = Arc::new(Mutex::new(Atlas::new(ctx, miniquad::FilterMode::Nearest)));
656        let font =
657            crate::text::Font::load_from_bytes(atlas.clone(), include_bytes!("ProggyClean.ttf"))
658                .unwrap();
659
660        for character in crate::text::Font::ascii_character_list() {
661            font.cache_glyph(character, 13);
662        }
663
664        atlas
665            .lock()
666            .unwrap()
667            .cache_sprite(SpriteKey::Id(0), Image::gen_image_color(1, 1, crate::WHITE));
668
669        let font = Arc::new(Mutex::new(font));
670        Ui {
671            input: Input::default(),
672            default_font: font.clone(),
673            skin_stack: StyleStack::new(atlas.clone(), font),
674            frame: 0,
675            moving: None,
676            windows: HashMap::default(),
677            modal: None,
678            root_window: {
679                let mut window = Window::new(
680                    0,
681                    None,
682                    Vec2::new(0., 0.),
683                    Vec2::new(screen_width, screen_height),
684                    0.0,
685                    RectOffset::new(0.0, 0.0, 0.0, 0.0),
686                    0.0,
687                    false,
688                    true,
689                    atlas.clone(),
690                );
691                window.active = true;
692                window.was_active = true;
693                window
694            },
695            windows_focus_order: vec![],
696            dragging: None,
697            active_window: None,
698            hovered_window: 0,
699            in_modal: false,
700            child_window_stack: vec![],
701            drag_hovered: None,
702            drag_hovered_previous_frame: None,
703            storage_u32: HashMap::default(),
704            storage_any: AnyStorage::default(),
705            atlas,
706            clipboard_selection: String::new(),
707            clipboard: Box::new(ui_context::ClipboardObject),
708            time: 0.0,
709            key_repeat: key_repeat::KeyRepeat::new(),
710            last_item_clicked: false,
711            last_item_hovered: false,
712            tab_selector: TabSelector::new(),
713            input_focus: None,
714        }
715    }
716
717    pub fn set_default_skin(&mut self, _skin: Skin) {
718        unimplemented!()
719    }
720
721    pub fn style_builder(&self) -> StyleBuilder {
722        StyleBuilder::new(self.default_font.clone(), self.atlas.clone())
723    }
724
725    pub fn default_skin(&self) -> Skin {
726        self.skin_stack.top().clone()
727    }
728
729    pub(crate) fn begin_window(
730        &mut self,
731        id: Id,
732        parent: Option<Id>,
733        position: Vec2,
734        size: Vec2,
735        titlebar: bool,
736        movable: bool,
737    ) -> WindowContext {
738        if parent.is_some() {
739            self.child_window_stack
740                .push(self.active_window.unwrap_or(0));
741        }
742        self.input.window_active = self.is_input_hovered(id);
743
744        self.active_window = Some(id);
745
746        let focused = self.is_focused(id);
747        let margin = self.skin_stack.top().margin;
748        let margin_window = self.skin_stack.top().window_style.border_margin();
749
750        let title_height = if titlebar {
751            self.skin_stack.top().title_height
752        } else {
753            0.
754        };
755        let atlas = self.atlas.clone();
756        let windows_focus_order = &mut self.windows_focus_order;
757
758        let parent_force_focus = match parent {
759            // childs of root window are always force_focused
760            Some(0) => true,
761            // childs of force_focused windows are always force_focused as well
762            Some(parent) => self
763                .windows
764                .get(&parent)
765                .map_or(false, |window| window.force_focus),
766            _ => false,
767        };
768        let parent_clip_rect = if let Some(parent) = parent {
769            self.windows
770                .get(&parent)
771                .and_then(|window| window.painter.clipping_zone)
772        } else {
773            None
774        };
775
776        let window = &mut *self.windows.entry(id).or_insert_with(|| {
777            if parent.is_none() {
778                windows_focus_order.push(id);
779            }
780
781            Window::new(
782                id,
783                parent,
784                position,
785                size,
786                title_height,
787                margin_window,
788                margin,
789                movable,
790                parent_force_focus,
791                atlas,
792            )
793        });
794        if !window.movable {
795            window.set_position(position);
796        }
797        window.size = size;
798        window.want_close = false;
799        window.active = true;
800        window.painter.clipping_zone = parent_clip_rect;
801
802        // top level windows are movable, so we update their position only on the first frame
803        // while the child windows are not movable and should update their position each frame
804        if parent.is_some() {
805            window.set_position(position);
806        }
807
808        WindowContext {
809            focused,
810            window,
811            input: &mut self.input,
812            style: self.skin_stack.top(),
813            dragging: &mut self.dragging,
814            drag_hovered: &mut self.drag_hovered,
815            drag_hovered_previous_frame: &mut self.drag_hovered_previous_frame,
816            storage_u32: &mut self.storage_u32,
817            storage_any: &mut self.storage_any,
818            clipboard_selection: &mut self.clipboard_selection,
819            clipboard: &mut *self.clipboard,
820            last_item_clicked: &mut self.last_item_clicked,
821            last_item_hovered: &mut self.last_item_hovered,
822            tab_selector: &mut self.tab_selector,
823            input_focus: &mut self.input_focus,
824        }
825    }
826
827    pub(crate) fn begin_modal(&mut self, id: Id, position: Vec2, size: Vec2) -> WindowContext {
828        self.input.window_active = true;
829        self.in_modal = true;
830
831        let atlas = self.atlas.clone();
832
833        let window = self.modal.get_or_insert_with(|| {
834            Window::new(
835                id,
836                None,
837                position,
838                size,
839                0.0,
840                RectOffset::new(0.0, 0.0, 0.0, 0.0),
841                0.0,
842                false,
843                true,
844                atlas,
845            )
846        });
847
848        window.parent = self.active_window;
849        window.size = size;
850        window.want_close = false;
851        window.active = true;
852        window.painter.clipping_zone = Some(Rect::new(position.x, position.y, size.x, size.y));
853        window.set_position(position);
854
855        WindowContext {
856            focused: true,
857            window,
858            input: &mut self.input,
859            style: self.skin_stack.top(),
860            dragging: &mut self.dragging,
861            drag_hovered: &mut self.drag_hovered,
862            drag_hovered_previous_frame: &mut self.drag_hovered_previous_frame,
863            storage_u32: &mut self.storage_u32,
864            storage_any: &mut self.storage_any,
865            clipboard_selection: &mut self.clipboard_selection,
866            clipboard: &mut *self.clipboard,
867            last_item_clicked: &mut self.last_item_clicked,
868            last_item_hovered: &mut self.last_item_hovered,
869            tab_selector: &mut self.tab_selector,
870            input_focus: &mut self.input_focus,
871        }
872    }
873
874    pub(crate) fn end_modal(&mut self) {
875        self.in_modal = false;
876        self.input.window_active = self.is_input_hovered(self.active_window.unwrap_or(0));
877    }
878
879    pub(crate) fn end_window(&mut self) {
880        self.active_window = self.child_window_stack.pop();
881        self.input.window_active = self.is_input_hovered(self.active_window.unwrap_or(0));
882    }
883
884    pub(crate) fn get_active_window_context(&mut self) -> WindowContext {
885        let focused;
886        let window = if self.in_modal == false {
887            match self.active_window {
888                None | Some(0) => {
889                    focused = true;
890                    &mut self.root_window
891                }
892                Some(active_window) => {
893                    focused = self.is_focused(active_window);
894                    self.windows.get_mut(&active_window).unwrap()
895                }
896            }
897        } else {
898            focused = true;
899            self.modal.as_mut().unwrap()
900        };
901
902        WindowContext {
903            window,
904            focused,
905            input: &mut self.input,
906            style: self.skin_stack.top(),
907            dragging: &mut self.dragging,
908            drag_hovered: &mut self.drag_hovered,
909            drag_hovered_previous_frame: &mut self.drag_hovered_previous_frame,
910            storage_u32: &mut self.storage_u32,
911            storage_any: &mut self.storage_any,
912            clipboard_selection: &mut self.clipboard_selection,
913            clipboard: &mut *self.clipboard,
914            last_item_clicked: &mut self.last_item_clicked,
915            last_item_hovered: &mut self.last_item_hovered,
916            tab_selector: &mut self.tab_selector,
917            input_focus: &mut self.input_focus,
918        }
919    }
920
921    /// Returns true if the last widget which had `.ui` called on it is being clicked.
922    pub fn last_item_clicked(&mut self) -> bool {
923        self.last_item_clicked
924    }
925
926    /// Returns true if the mouse is over the last widget which had `.ui` called on it.
927    pub fn last_item_hovered(&mut self) -> bool {
928        self.last_item_hovered
929    }
930
931    /// Scrolls the middle of the active GUI window to its GUI cursor
932    ///
933    /// Note that this does not work on the first frame of the GUI application.
934    /// If you want your widget to start with its scrollbar in a particular location,
935    /// consider `if ui.frame == 1 { ui.scroll_here() }`.
936    pub fn scroll_here(&mut self) {
937        self.scroll_here_ratio(0.5);
938    }
939
940    /// Scrolls the active GUI window to its GUI cursor.
941    ///
942    /// 1.0 puts the bottom of the window at the GUI cursor,
943    /// 0.0 puts the top of the window there.
944    ///
945    /// 0.5 as the ratio puts the middle of the window at the GUI cursor,
946    /// and is equivalent to `Ui::scroll_here`.
947    pub fn scroll_here_ratio(&mut self, ratio: f32) {
948        let context = self.get_active_window_context();
949        let cursor = &mut context.window.cursor;
950        cursor.scroll.scroll_to(cursor.y - cursor.area.h * ratio);
951    }
952
953    /// How far the active gui window has been scrolled down on the y axis.
954    ///
955    /// Note that for these purposes, a Group widget is still considered a Window
956    /// because it can have its own scrollbar.
957    pub fn scroll(&mut self) -> Vec2 {
958        self.get_active_window_context().window.cursor.scroll.scroll
959    }
960
961    /// The farthest down a scrollbar may go given the constraints of its window.
962    ///
963    /// Note that for these purposes, a Group widget is still considered a Window
964    /// because it can have its own scrollbar.
965    pub fn scroll_max(&mut self) -> Vec2 {
966        let cursor = &self.get_active_window_context().window.cursor;
967        Vec2::new(
968            cursor.scroll.inner_rect.w - cursor.area.w,
969            cursor.scroll.inner_rect.h - cursor.area.h,
970        )
971    }
972
973    pub const fn is_mouse_captured(&self) -> bool {
974        self.input.cursor_grabbed
975    }
976
977    pub fn is_mouse_over(&self, mouse_position: Vec2) -> bool {
978        for window in self.windows_focus_order.iter() {
979            let window = &self.windows[window];
980            if window.was_active == false {
981                continue;
982            }
983            if window.full_rect().contains(mouse_position) {
984                return true;
985            }
986        }
987        if let Some(window) = &self.modal {
988            if window.was_active {
989                if window.full_rect().contains(mouse_position) {
990                    return true;
991                }
992            }
993        }
994        false
995    }
996
997    pub fn active_window_focused(&self) -> bool {
998        self.active_window.map_or(false, |wnd| self.is_focused(wnd))
999    }
1000
1001    pub const fn is_dragging(&self) -> bool {
1002        self.dragging.is_some()
1003    }
1004
1005    pub fn close_current_window(&mut self) {
1006        let mut context = self.get_active_window_context();
1007        context.close();
1008    }
1009
1010    fn is_input_hovered(&self, id: Id) -> bool {
1011        // if thats exactly the clicked window - it's always the hovered one
1012        if id == self.hovered_window {
1013            return true;
1014        }
1015
1016        // hovered window is always the root window and the given id may be the child
1017        // window id
1018        // so need to figure the root id
1019
1020        if self.in_modal {
1021            true
1022        } else {
1023            self.child_window_stack
1024                .get(0)
1025                .map_or(false, |root| *root == self.hovered_window)
1026        }
1027    }
1028
1029    fn is_focused(&self, id: Id) -> bool {
1030        if self
1031            .windows
1032            .get(&id)
1033            .map_or(false, |window| window.force_focus)
1034        {
1035            return true;
1036        }
1037
1038        if let Some(focused_window) = self
1039            .windows_focus_order
1040            .iter()
1041            .find(|window| self.windows[window].was_active || self.windows[window].active)
1042        {
1043            if id == *focused_window {
1044                return true;
1045            }
1046            if let Some(parent) = self.child_window_stack.get(0) {
1047                return *parent == *focused_window;
1048            }
1049        }
1050
1051        false
1052    }
1053
1054    pub fn new_frame(&mut self, delta: f32) {
1055        self.root_window.resize(crate::math::vec2(
1056            crate::window::screen_width(),
1057            crate::window::screen_height(),
1058        ));
1059
1060        self.frame += 1;
1061        self.time += delta;
1062
1063        self.last_item_clicked = false;
1064        self.last_item_hovered = false;
1065
1066        self.drag_hovered_previous_frame = self.drag_hovered;
1067        self.drag_hovered = None;
1068        self.input.reset();
1069        self.input.window_active = self.hovered_window == 0;
1070
1071        self.tab_selector.new_frame();
1072
1073        self.key_repeat.new_frame(self.time);
1074
1075        for (_, window) in &mut self.windows {
1076            window.painter.clear();
1077            window.cursor.reset();
1078            window.was_active = window.active;
1079            window.active = false;
1080            window.childs.clear();
1081        }
1082
1083        if let Some(window) = &mut self.modal {
1084            window.painter.clear();
1085            window.cursor.reset();
1086            window.was_active = window.active;
1087            window.active = false;
1088            window.childs.clear();
1089        }
1090
1091        {
1092            self.root_window.painter.clear();
1093            self.root_window.cursor.reset();
1094            self.root_window.childs.clear();
1095        }
1096    }
1097
1098    pub fn render(&mut self, draw_list: &mut Vec<DrawList>) {
1099        self.render_window(&self.root_window, Vec2::new(0., 0.), draw_list);
1100
1101        for window in self.windows_focus_order.iter().rev() {
1102            let window = &self.windows[window];
1103            if window.was_active {
1104                self.render_window(window, Vec2::new(0., 0.), draw_list);
1105            }
1106        }
1107
1108        if let Some(modal) = self.modal.as_ref() {
1109            if modal.was_active {
1110                self.render_window(modal, Vec2::new(0., 0.), draw_list);
1111            }
1112        }
1113
1114        if let Some((id, DragState::Dragging(orig))) = self.dragging {
1115            let window = &self.windows[&id];
1116
1117            self.render_window(window, self.input.mouse_position - orig, draw_list);
1118        }
1119    }
1120
1121    fn render_window(&self, window: &Window, offset: Vec2, draw_list: &mut Vec<DrawList>) {
1122        for cmd in &window.painter.commands {
1123            crate::ui::render::render_command(draw_list, cmd.offset(offset));
1124        }
1125
1126        for child in &window.childs {
1127            let child_window = &self.windows[child];
1128            if window.content_rect().overlaps(&child_window.full_rect()) {
1129                self.render_window(child_window, offset, draw_list);
1130            }
1131        }
1132    }
1133
1134    pub fn focus_window(&mut self, id: Id) {
1135        if let Some(n) = self.windows_focus_order.iter().position(|win| *win == id) {
1136            let window = self.windows_focus_order.remove(n);
1137            self.windows_focus_order.insert(0, window);
1138        }
1139    }
1140
1141    pub fn set_input_focus(&mut self, id: Id) {
1142        self.input_focus = Some(id);
1143    }
1144
1145    pub fn clear_input_focus(&mut self) {
1146        self.input_focus = None;
1147    }
1148
1149    pub fn move_window(&mut self, id: Id, position: Vec2) {
1150        if let Some(window) = self.windows.get_mut(&id) {
1151            window.set_position(position);
1152        }
1153    }
1154
1155    pub fn same_line(&mut self, x: f32) {
1156        let context = self.get_active_window_context();
1157        context.window.same_line(x);
1158    }
1159
1160    pub fn canvas(&mut self) -> DrawCanvas {
1161        let context = self.get_active_window_context();
1162
1163        DrawCanvas { context }
1164    }
1165
1166    /// small hack to keep some internal state
1167    /// used like this:
1168    /// ```skip
1169    /// if ui.last_item_clicked() {
1170    ///     *ui.get_bool(hash!("color picker opened")) ^= true;
1171    /// }
1172    /// if *ui.get_bool(hash!("color picker opened"))  {
1173    /// }
1174    /// ```
1175    pub fn get_bool(&mut self, id: Id) -> &mut bool {
1176        self.storage_any.get_or_default(id)
1177    }
1178
1179    pub fn get_any<T: std::any::Any + Default>(&mut self, id: Id) -> &mut T {
1180        self.storage_any.get_or_default(id)
1181    }
1182
1183    pub fn push_skin(&mut self, skin: &Skin) {
1184        self.skin_stack.custom_skin_stack.push(skin.clone());
1185    }
1186
1187    pub fn pop_skin(&mut self) {
1188        self.skin_stack.custom_skin_stack.pop();
1189    }
1190}
1191
1192pub(crate) mod ui_context {
1193    use crate::prelude::*;
1194    use crate::window::miniquad::*;
1195
1196    use crate::ui as megaui;
1197
1198    use std::cell::RefCell;
1199    use std::rc::Rc;
1200
1201    pub(crate) struct UiContext {
1202        pub ui: Rc<RefCell<megaui::Ui>>,
1203        ui_draw_list: Vec<megaui::DrawList>,
1204        material: Option<Material>,
1205    }
1206
1207    impl UiContext {
1208        pub(crate) fn new(
1209            ctx: &mut dyn miniquad::RenderingBackend,
1210            screen_width: f32,
1211            screen_height: f32,
1212        ) -> UiContext {
1213            let ui = megaui::Ui::new(ctx, screen_width, screen_height);
1214
1215            UiContext {
1216                ui: Rc::new(RefCell::new(ui)),
1217                ui_draw_list: vec![],
1218                material: None,
1219            }
1220        }
1221
1222        pub(crate) fn process_input(&mut self) {
1223            use megaui::InputHandler;
1224
1225            let mouse_position = mouse_position();
1226
1227            let mut ui = self.ui.borrow_mut();
1228            ui.mouse_move(mouse_position);
1229
1230            if is_mouse_button_pressed(MouseButton::Left) {
1231                ui.mouse_down(mouse_position);
1232            }
1233            if is_mouse_button_released(MouseButton::Left) {
1234                ui.mouse_up(mouse_position);
1235            }
1236
1237            let shift = is_key_down(KeyCode::LeftShift) || is_key_down(KeyCode::RightShift);
1238            let ctrl = is_key_down(KeyCode::LeftControl) || is_key_down(KeyCode::RightControl);
1239
1240            while let Some(c) = get_char_pressed_ui() {
1241                if ctrl == false {
1242                    ui.char_event(c, false, false);
1243                }
1244            }
1245
1246            macro_rules! process {
1247                ($code:tt) => {
1248                    if is_key_pressed(KeyCode::$code) || is_key_down(KeyCode::$code) {
1249                        ui.key_down(megaui::KeyCode::$code, shift, ctrl);
1250                    }
1251                };
1252            }
1253
1254            process!(Up);
1255            process!(Down);
1256            process!(Right);
1257            process!(Left);
1258            process!(Home);
1259            process!(End);
1260            process!(Delete);
1261            process!(Backspace);
1262            process!(Tab);
1263            process!(Z);
1264            process!(Y);
1265            process!(C);
1266            process!(X);
1267            process!(V);
1268            process!(A);
1269            process!(Escape);
1270            process!(Enter);
1271
1272            if is_key_down(KeyCode::LeftControl)
1273                || is_key_down(KeyCode::RightControl)
1274                || is_key_pressed(KeyCode::LeftControl)
1275                || is_key_pressed(KeyCode::RightControl)
1276            {
1277                ui.key_down(megaui::KeyCode::Control, shift, ctrl);
1278            }
1279            let (wheel_x, wheel_y) = mouse_wheel();
1280            ui.mouse_wheel(wheel_x, -wheel_y);
1281        }
1282
1283        pub(crate) fn draw(
1284            &mut self,
1285            ctx: &mut dyn miniquad::RenderingBackend,
1286            quad_gl: &mut QuadGl,
1287        ) {
1288            // TODO: this belongs to new and waits for cleaning up context initialization mess
1289            let material = self.material.get_or_insert_with(|| {
1290                load_material(
1291                    match ctx.info().backend {
1292                        Backend::OpenGl => ShaderSource::Glsl {
1293                            vertex: VERTEX_SHADER,
1294                            fragment: FRAGMENT_SHADER,
1295                        },
1296                        Backend::Metal => ShaderSource::Msl {
1297                            program: METAL_SHADER,
1298                        },
1299                    },
1300                    MaterialParams {
1301                        pipeline_params: PipelineParams {
1302                            color_blend: Some(BlendState::new(
1303                                Equation::Add,
1304                                BlendFactor::Value(BlendValue::SourceAlpha),
1305                                BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
1306                            )),
1307                            ..Default::default()
1308                        },
1309                        ..Default::default()
1310                    },
1311                )
1312                .unwrap()
1313            });
1314
1315            let mut ui = self.ui.borrow_mut();
1316            self.ui_draw_list.clear();
1317            ui.render(&mut self.ui_draw_list);
1318            let mut ui_draw_list = vec![];
1319
1320            std::mem::swap(&mut ui_draw_list, &mut self.ui_draw_list);
1321
1322            let mut atlas = ui.atlas.lock().unwrap();
1323            let font_texture = atlas.texture();
1324            quad_gl.texture(Some(&Texture2D::unmanaged(font_texture)));
1325
1326            gl_use_material(material);
1327
1328            for draw_command in &ui_draw_list {
1329                if let Some(ref texture) = draw_command.texture {
1330                    quad_gl.texture(Some(texture));
1331                } else {
1332                    quad_gl.texture(Some(&Texture2D::unmanaged(font_texture)));
1333                }
1334
1335                quad_gl.scissor(
1336                    draw_command
1337                        .clipping_zone
1338                        .map(|rect| (rect.x as i32, rect.y as i32, rect.w as i32, rect.h as i32)),
1339                );
1340                quad_gl.draw_mode(DrawMode::Triangles);
1341                quad_gl.geometry(&draw_command.vertices[..], &draw_command.indices);
1342            }
1343            quad_gl.texture(None);
1344
1345            gl_use_default_material();
1346
1347            std::mem::swap(&mut ui_draw_list, &mut self.ui_draw_list);
1348
1349            drop(atlas);
1350            ui.new_frame(get_frame_time());
1351        }
1352    }
1353
1354    pub struct ClipboardObject;
1355
1356    impl megaui::ClipboardObject for ClipboardObject {
1357        fn get(&self) -> Option<String> {
1358            miniquad::window::clipboard_get()
1359        }
1360
1361        fn set(&mut self, data: &str) {
1362            miniquad::window::clipboard_set(data);
1363        }
1364    }
1365
1366    const VERTEX_SHADER: &'static str = "#version 100
1367attribute vec3 position;
1368attribute vec4 color0;
1369attribute vec2 texcoord;
1370
1371varying lowp vec2 uv;
1372varying lowp vec2 pos;
1373varying lowp vec4 color;
1374
1375uniform mat4 Model;
1376uniform mat4 Projection;
1377
1378void main() {
1379    gl_Position = Projection * Model * vec4(position, 1);
1380    uv = texcoord;
1381    color = color0 / 255.0;
1382}
1383";
1384    const FRAGMENT_SHADER: &'static str = "#version 100
1385varying lowp vec2 uv;
1386varying lowp vec4 color;
1387
1388uniform sampler2D Texture;
1389
1390void main() {
1391    gl_FragColor = texture2D(Texture, uv) * color;
1392}
1393";
1394    pub const METAL_SHADER: &str = r#"
1395#include <metal_stdlib>
1396    using namespace metal;
1397
1398    struct Uniforms
1399    {
1400        float4x4 Model;
1401        float4x4 Projection;
1402    };
1403
1404    struct Vertex
1405    {
1406        float3 position    [[attribute(0)]];
1407        float2 texcoord    [[attribute(1)]];
1408        float4 color0      [[attribute(2)]];
1409    };
1410
1411    struct RasterizerData
1412    {
1413        float4 position [[position]];
1414        float4 color [[user(locn0)]];
1415        float2 uv [[user(locn1)]];
1416    };
1417
1418    vertex RasterizerData vertexShader(Vertex v [[stage_in]], constant Uniforms& uniforms [[buffer(0)]])
1419    {
1420        RasterizerData out;
1421
1422        out.position = uniforms.Model * uniforms.Projection * float4(v.position, 1);
1423        out.color = v.color0 / 255.0;
1424        out.uv = v.texcoord;
1425
1426        return out;
1427    }
1428
1429    fragment float4 fragmentShader(RasterizerData in [[stage_in]], texture2d<float> tex [[texture(0)]], sampler texSmplr [[sampler(0)]])
1430    {
1431        return in.color * tex.sample(texSmplr, in.uv);
1432    }"#;
1433}