1pub 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
40pub fn root_ui() -> impl DerefMut<Target = Ui> {
43 crate::get_context().ui_context.ui.borrow_mut()
44}
45
46#[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
87pub 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 pub active: bool,
120 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 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 pub frame: u64,
340 pub(crate) time: f32,
341
342 moving: Option<(Id, Vec2)>,
343 windows: HashMap<Id, Window>,
344 modal: Option<Window>,
347 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 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 Some(0) => true,
761 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 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 pub fn last_item_clicked(&mut self) -> bool {
923 self.last_item_clicked
924 }
925
926 pub fn last_item_hovered(&mut self) -> bool {
928 self.last_item_hovered
929 }
930
931 pub fn scroll_here(&mut self) {
937 self.scroll_here_ratio(0.5);
938 }
939
940 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 pub fn scroll(&mut self) -> Vec2 {
958 self.get_active_window_context().window.cursor.scroll.scroll
959 }
960
961 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 id == self.hovered_window {
1013 return true;
1014 }
1015
1016 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 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 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}