1use crate::*;
2
3use crate::math::Axis::*;
4
5use arboard::Clipboard;
6use basic_window_loop::basic_depth_stencil_state;
7use glam::DVec2;
8use glyphon::Cache as GlyphonCache;
9use glyphon::Viewport;
10
11use slab::Slab;
12use wgpu::{
13 BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry,
14 BindingResource, BindingType, BlendState, Buffer, BufferBindingType, ColorWrites, FilterMode,
15 FragmentState, PipelineLayoutDescriptor, PrimitiveState, RenderPipelineDescriptor,
16 SamplerBindingType, SamplerDescriptor, ShaderModuleDescriptor, ShaderSource, ShaderStages,
17 TextureSampleType, TextureViewDimension, VertexState,
18};
19use winit::dpi::PhysicalSize;
20use winit_key_events::KeyInput;
21use winit_mouse_events::MouseInput;
22
23use std::sync::atomic::AtomicU64;
24use std::sync::atomic::Ordering;
25use std::sync::LazyLock;
26use std::time::Duration;
27use std::{mem, time::Instant};
28
29use bytemuck::{Pod, Zeroable};
30use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
31use wgpu::{
32 util::{self, DeviceExt},
33 BindGroup, BufferAddress, BufferUsages, ColorTargetState, Device, MultisampleState, Queue,
34 RenderPipeline, SurfaceConfiguration, VertexBufferLayout, VertexStepMode,
35};
36
37pub(crate) static T0: LazyLock<Instant> = LazyLock::new(Instant::now);
38
39pub(crate) fn ui_time_f32() -> f32 {
40 return T0.elapsed().as_secs_f32();
41}
42
43pub struct Ui {
55 pub(crate) nodes: Nodes,
56 pub(crate) sys: System,
57 pub(crate) format_scratch: String,
58}
59
60static INSTANCE_COUNTER: AtomicU64 = AtomicU64::new(1);
61
62pub(crate) struct System {
63 pub inspect_mode: bool,
66
67 pub unique_id: u64,
68 pub theme: Theme,
69 pub debug_key_pressed: bool,
70
71 pub new_ui_input: u8,
72 pub new_external_events: bool,
73
74 pub clipboard: Clipboard,
75
76 pub gpu_rect_buffer: TypedGpuBuffer<RenderRect>,
77 pub render_pipeline: RenderPipeline,
78
79 pub base_uniform_buffer: Buffer,
80 pub bind_group: BindGroup,
81
82 pub text: TextSystem,
83 pub texture_atlas: TextureAtlas,
84
85 pub z_cursor: f32,
86 pub rects: Vec<RenderRect>,
87 pub editor_rects_i: u16,
88
89 pub click_rects: Vec<ClickRect>,
90
91 pub scroll_rects: Vec<ClickRect>,
92
93 pub unifs: Uniforms,
94 pub current_frame: u64,
95 pub last_frame_end_fake_time: u64,
96 pub second_last_frame_end_fake_time: u64,
97 pub third_last_frame_end_fake_time: u64,
98
99 pub mouse_hit_stack: Vec<(Id, f32)>,
100
101 pub mouse_input: MouseInput<Id>,
103 pub key_input: KeyInput,
104
105 #[cfg(debug_assertions)]
106 pub inspect_hovered: Option<Id>,
107
108 pub hovered: Vec<Id>,
109 pub hovered_scroll_area: Option<Id>,
110
111 pub focused: Option<Id>,
112
113 pub partial_relayout_count: u32,
115
116 pub old_child_collect: Vec<NodeI>,
117 pub new_child_collect: Vec<NodeI>,
118 pub added_nodes: Vec<NodeI>,
119 pub direct_removed_nodes: Vec<NodeI>,
121 pub indirect_removed_nodes: Vec<NodeI>,
123 pub hidden_nodes: Vec<NodeI>,
125
126 pub very_indirect_removed_nodes: Vec<NodeI>,
131
132
133 pub changes: PartialChanges,
134
135 pub anim_render_timer: AnimationRenderTimer,
137
138 pub hidden_stack: Vec<NodeI>,
140}
141
142pub(crate) struct AnimationRenderTimer(Option<Instant>);
143
144impl AnimationRenderTimer {
145 fn default() -> Self {
146 Self(None)
147 }
148
149 pub(crate) fn push_new(&mut self, duration: Duration) {
150 let now = Instant::now();
151 let new_end = now + duration;
152
153 if let Some(end) = self.0 {
154 if new_end > end {
155 *self = AnimationRenderTimer(Some(new_end));
156 }
157 } else {
158 *self = AnimationRenderTimer(Some(new_end));
159 }
160 }
161
162 pub(crate) fn is_live(&mut self) -> bool {
163 if let Some(end) = self.0 {
164 let is_live = Instant::now() < end;
165 if !is_live {
166 *self = AnimationRenderTimer(None);
167 }
168 return is_live;
169 }
170 false
171 }
172}
173
174#[repr(C)]
175#[derive(Debug, Pod, Copy, Clone, Zeroable)]
176pub(crate) struct Uniforms {
177 pub size: Xy<f32>,
178 pub t: f32,
179 pub _padding: f32,
180}
181
182impl Ui {
183 pub fn new(device: &Device, queue: &Queue, config: &SurfaceConfiguration) -> Self {
184 LazyLock::force(&T0);
186
187 let gpu_rect_buffer = device.create_buffer_init(&util::BufferInitDescriptor {
188 label: Some("Keru rectangle buffer"),
189 contents: {
191 bytemuck::cast_slice(&[0.0; 2048])
192 },
193 usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
194 });
195
196 let gpu_rect_buffer = TypedGpuBuffer::new(gpu_rect_buffer);
197 let vert_buff_layout = VertexBufferLayout {
198 array_stride: mem::size_of::<RenderRect>() as BufferAddress,
199 step_mode: VertexStepMode::Instance,
200 attributes: &RenderRect::buffer_desc(),
201 };
202
203 let uniforms = Uniforms {
204 size: Xy::new(config.width as f32, config.height as f32),
205 t: 0.,
206 _padding: 0.,
207 };
208 let resolution_buffer = device.create_buffer_init(&util::BufferInitDescriptor {
209 label: Some("Resolution Uniform Buffer"),
210 contents: bytemuck::bytes_of(&uniforms),
211 usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
212 });
213
214 let mut texture_atlas = TextureAtlas::new(device);
215
216 let _white_alloc = texture_atlas.allocate_image(include_bytes!("textures/white.png"));
217 let texture_sampler = device.create_sampler(&SamplerDescriptor {
220 label: Some("Texture sampler"),
221 min_filter: FilterMode::Nearest,
222 mag_filter: FilterMode::Nearest,
223 mipmap_filter: FilterMode::Nearest,
224 lod_min_clamp: 0f32,
225 lod_max_clamp: 0f32,
226 ..Default::default()
227 });
228
229 let bind_group_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
230 entries: &[
231 BindGroupLayoutEntry {
232 binding: 0,
233 visibility: ShaderStages::VERTEX,
234 ty: BindingType::Buffer {
235 ty: BufferBindingType::Uniform,
236 has_dynamic_offset: false,
237 min_binding_size: None,
238 },
239 count: None,
240 },
241 BindGroupLayoutEntry {
242 binding: 1,
243 visibility: ShaderStages::FRAGMENT,
244 ty: BindingType::Texture {
245 multisampled: false,
246 view_dimension: TextureViewDimension::D2,
247 sample_type: TextureSampleType::Float { filterable: true },
248 },
249 count: None,
250 },
251 BindGroupLayoutEntry {
252 binding: 2,
253 visibility: ShaderStages::FRAGMENT,
254 ty: BindingType::Sampler(SamplerBindingType::Filtering),
255 count: None,
256 },
257 ],
258 label: Some("Keru Bind Group Layout"),
259 });
260
261 let bind_group = device.create_bind_group(&BindGroupDescriptor {
263 layout: &bind_group_layout,
264 entries: &[
265 BindGroupEntry {
266 binding: 0,
267 resource: resolution_buffer.as_entire_binding(),
268 },
269 BindGroupEntry {
270 binding: 1,
271 resource: BindingResource::TextureView(texture_atlas.texture_view()),
272 },
273 BindGroupEntry {
274 binding: 2,
275 resource: BindingResource::Sampler(&texture_sampler),
276 },
277 ],
278 label: Some("Keru Bind Group"),
279 });
280
281 let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
282 label: None,
283 bind_group_layouts: &[&bind_group_layout],
284 push_constant_ranges: &[],
285 });
286
287 let shader = device.create_shader_module(ShaderModuleDescriptor {
288 label: None,
289 source: ShaderSource::Wgsl(include_str!("shaders/box.wgsl").into()),
290 });
291
292 let primitive = PrimitiveState::default();
293
294 let render_pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
295 label: None,
296 layout: Some(&pipeline_layout),
297 vertex: VertexState {
298 module: &shader,
299 entry_point: Some("vs_main"),
300 buffers: &[vert_buff_layout],
301 compilation_options: Default::default(),
302 },
303 fragment: Some(FragmentState {
304 module: &shader,
305 entry_point: Some("fs_main"),
306 targets: &[Some(ColorTargetState {
307 format: config.format,
308 blend: Some(BlendState::ALPHA_BLENDING),
309 write_mask: ColorWrites::ALL,
310 })],
311 compilation_options: Default::default(),
312 }),
313 primitive,
314 depth_stencil: Some(basic_depth_stencil_state()),
315 multisample: MultisampleState::default(),
316 multiview: None,
317 cache: None,
318 });
319
320 let font_system = FontSystem::new();
321 let cache = SwashCache::new();
322 let glyphon_cache = GlyphonCache::new(device);
323 let glyphon_viewport = Viewport::new(device, &glyphon_cache);
324
325 let mut atlas = TextAtlas::new(device, queue, &glyphon_cache, config.format);
326 let text_renderer =
327 TextRenderer::new(&mut atlas, device, MultisampleState::default(), Some(basic_depth_stencil_state()));
328
329 let nodes = Nodes::new();
330
331 let third_last_frame_end_fake_time = 3;
332 let second_last_frame_end_fake_time = 4;
333 let last_frame_end_fake_time = 5;
334
335 Self {
336 nodes,
337 format_scratch: String::with_capacity(1024),
338
339 sys: System {
340 unique_id: INSTANCE_COUNTER.fetch_add(1, Ordering::Relaxed),
341 z_cursor: 0.0,
342 theme: KERU_DARK,
343 inspect_mode: false,
344 debug_key_pressed: false,
345
346 new_ui_input: 2,
347 new_external_events: true,
348
349 clipboard: Clipboard::new().expect("Couldn't initialize clipboard"),
350
351 text: TextSystem {
352 cache,
353 atlas,
354 text_renderer,
355 font_system,
356 slabs: TextSlabs {
357 boxes: Vec::with_capacity(50),
358 editors: Slab::with_capacity(10),
359 },
360 glyphon_viewport,
361 },
362
363 texture_atlas,
364
365 render_pipeline,
366 rects: Vec::with_capacity(100),
367 editor_rects_i: 0,
368
369 click_rects: Vec::with_capacity(50),
370 scroll_rects: Vec::with_capacity(20),
371
372 gpu_rect_buffer,
373 base_uniform_buffer: resolution_buffer,
374 bind_group,
375
376 partial_relayout_count: 0,
377
378 current_frame: FIRST_FRAME,
379 third_last_frame_end_fake_time,
380 second_last_frame_end_fake_time,
381 last_frame_end_fake_time,
382
383 unifs: uniforms,
384
385 mouse_hit_stack: Vec::with_capacity(50),
386
387 mouse_input: MouseInput::default(),
388 key_input: KeyInput::default(),
389
390 hovered: Vec::with_capacity(15),
392 hovered_scroll_area: None,
393
394 #[cfg(debug_assertions)]
395 inspect_hovered: None,
396
397 old_child_collect: Vec::with_capacity(10),
398 new_child_collect: Vec::with_capacity(10),
399 added_nodes: Vec::with_capacity(30),
400 direct_removed_nodes: Vec::with_capacity(30),
401 indirect_removed_nodes: Vec::with_capacity(30),
402 very_indirect_removed_nodes: Vec::with_capacity(30),
403
404 focused: None,
405
406 anim_render_timer: AnimationRenderTimer::default(),
407
408 changes: PartialChanges::new(),
409 hidden_stack: Vec::with_capacity(10),
410 hidden_nodes: Vec::with_capacity(10),
411 },
412 }
413 }
414
415 pub fn base_uniform_buffer(&self) -> &Buffer {
427 return &self.sys.base_uniform_buffer;
428 }
429
430 pub fn set_inspect_mode(&mut self, inspect_mode: bool) {
432 if self.inspect_mode() != inspect_mode {
433 self.sys.changes.tree_changed = true;
434 }
435 self.sys.inspect_mode = inspect_mode;
436 }
437
438 pub fn inspect_mode(&self) -> bool {
441 return self.sys.inspect_mode;
442 }
443
444 pub fn theme(&mut self) -> &mut Theme {
446 return &mut self.sys.theme;
447 }
448
449 pub fn current_frame(&self) -> u64 {
450 return self.sys.current_frame;
451 }
452
453 pub fn unique_id(&self) -> u64 {
454 return self.sys.unique_id;
455 }
456
457 pub fn push_external_event(&mut self) {
458 self.sys.new_external_events = true;
459 }
460
461 pub fn needs_update(&mut self) -> bool {
469 return self.sys.new_ui_input > 0 ||
470 self.sys.new_external_events;
471 }
472
473 pub fn event_loop_needs_to_wake(&mut self) -> bool {
479 return self.needs_update() || self.needs_rerender();
480 }
481
482 pub fn cursor_position(&self) -> DVec2 {
483 return self.sys.mouse_input.cursor_position();
484 }
485
486 pub fn key_input(&self) -> &KeyInput {
488 return &self.sys.key_input;
489 }
490
491 pub(crate) fn set_new_ui_input(&mut self) {
492 self.sys.new_ui_input = 2;
495 }
496
497 pub(crate) fn resize(&mut self, size: &PhysicalSize<u32>) {
501 self.sys.changes.full_relayout = true;
502
503 self.sys.unifs.size[X] = size.width as f32;
504 self.sys.unifs.size[Y] = size.height as f32;
505
506 self.sys.changes.resize = true;
507 self.set_new_ui_input();
508 }
509}
510
511impl Ui {
512 pub(crate) fn hit_click_rect(&self, rect: &ClickRect) -> bool {
513 let size = self.sys.unifs.size;
514 let cursor_pos = (
515 self.cursor_position().x as f32 / size[X],
516 self.cursor_position().y as f32 / size[Y],
517 );
518
519 let aabb_hit = rect.rect[X][0] < cursor_pos.0
520 && cursor_pos.0 < rect.rect[X][1]
521 && rect.rect[Y][0] < cursor_pos.1
522 && cursor_pos.1 < rect.rect[Y][1];
523
524 if aabb_hit == false {
525 return false;
526 }
527
528 let node_i = rect.i;
529
530
531 match self.nodes[node_i].params.rect.shape {
532 Shape::Rectangle { corner_radius: _ } => {
533 return true;
534 }
535 Shape::Circle => {
536 let center_x = (rect.rect[X][0] + rect.rect[X][1]) / 2.0;
538 let center_y = (rect.rect[Y][0] + rect.rect[Y][1]) / 2.0;
539 let radius = (rect.rect[X][1] - rect.rect[X][0]) / 2.0;
540
541 let dx = cursor_pos.0 - center_x;
543 let dy = cursor_pos.1 - center_y;
544 return dx * dx + dy * dy <= radius * radius;
545 }
546 Shape::Ring { width } => {
547 let width = width / size[X];
550
551 let aspect = size[X] / size[Y];
552 let center_x = (rect.rect[X][0] + rect.rect[X][1]) / 2.0;
554 let center_y = (rect.rect[Y][0] + rect.rect[Y][1]) / 2.0;
555 let outer_radius = (rect.rect[X][1] - rect.rect[X][0]) / 2.0;
556 let inner_radius = outer_radius - width;
557
558 let dx = cursor_pos.0 - center_x;
560 let dy = (cursor_pos.1 - center_y) / aspect;
561 let distance_squared = dx * dx + dy * dy;
562 return distance_squared <= outer_radius * outer_radius
563 && distance_squared >= inner_radius * inner_radius;
564
565 }
566 }
567
568 }
569
570 pub(crate) fn set_static_image(&mut self, i: NodeI, image: &'static [u8]) -> &mut Self {
571 let image_pointer: *const u8 = image.as_ptr();
572
573 if let Some(last_pointer) = self.nodes[i].last_static_image_ptr {
574 if image_pointer == last_pointer {
575 return self;
576 }
577 }
578
579 let image = self.sys.texture_atlas.allocate_image(image);
580 self.nodes[i].imageref = Some(image);
581 self.nodes[i].last_static_image_ptr = Some(image_pointer);
582
583 return self;
584 }
585}