use glam::Vec4; use std::sync::Arc; use vulkano::image::view::ImageView; use crate::{ backend::input::PointerMode, graphics::WlxCommandBuffer, gui::GuiColor, state::AppState, }; use super::{CanvasData, Rect}; pub type ControlRenderer = fn(&Control, &CanvasData, &mut AppState, &mut WlxCommandBuffer) -> anyhow::Result<()>; pub type ControlRendererHl = fn( &Control, &CanvasData, &mut AppState, &mut WlxCommandBuffer, Vec4, ) -> anyhow::Result<()>; #[allow(clippy::type_complexity)] pub(crate) struct Control { pub state: Option, pub rect: Rect, pub corner_radius: f32, pub fg_color: GuiColor, pub bg_color: GuiColor, pub text: Arc, pub size: isize, pub sprite: Option>, pub sprite_st: Vec4, pub(super) dirty: bool, pub on_update: Option, pub on_press: Option, pub on_release: Option, pub on_scroll: Option, pub test_highlight: Option Option>, pub(super) on_render_bg: Option>, pub(super) on_render_hl: Option>, pub(super) on_render_fg: Option>, } impl Control { pub(super) fn new() -> Self { Self { rect: Rect { x: 0., y: 0., w: 0., h: 0., }, corner_radius: 0., fg_color: Vec4::ONE, bg_color: Vec4::ZERO, text: Arc::from(""), sprite: None, sprite_st: Vec4::new(1., 1., 0., 0.), dirty: true, size: 24, state: None, on_update: None, on_render_bg: None, on_render_hl: None, on_render_fg: None, test_highlight: None, on_press: None, on_release: None, on_scroll: None, } } #[inline(always)] pub fn set_text(&mut self, text: &str) { if *self.text == *text { return; } self.text = text.into(); self.dirty = true; } #[inline(always)] pub fn set_sprite(&mut self, sprite: Arc) { self.sprite.replace(sprite); self.dirty = true; } #[inline(always)] pub fn set_sprite_st(&mut self, sprite_st: Vec4) { if self.sprite_st == sprite_st { return; } self.sprite_st = sprite_st; self.dirty = true; } #[inline(always)] pub fn set_fg_color(&mut self, color: GuiColor) { if self.fg_color == color { return; } self.fg_color = color; self.dirty = true; } pub fn render_rounded_rect( &self, canvas: &CanvasData, _: &mut AppState, cmd_buffer: &mut WlxCommandBuffer, ) -> anyhow::Result<()> { let pass = { let vertex_buffer = canvas.graphics.upload_verts( canvas.width as _, canvas.height as _, self.rect.x, self.rect.y, self.rect.w, self.rect.h, )?; let clamped_radius = self .corner_radius .min(self.rect.w / 2.0) .min(self.rect.h / 2.0); let skew_radius = [clamped_radius / self.rect.w, clamped_radius / self.rect.h]; let set0 = canvas.pipeline_bg_color.uniform_buffer( 0, vec![ self.bg_color.x, self.bg_color.y, self.bg_color.z, self.bg_color.w, skew_radius[0], skew_radius[1], ], )?; canvas.pipeline_bg_color.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer, canvas.graphics.quad_indices.clone(), vec![set0], )? }; cmd_buffer.run_ref(&pass) } pub(super) fn render_highlight( &self, canvas: &CanvasData, _: &mut AppState, cmd_buffer: &mut WlxCommandBuffer, color: GuiColor, ) -> anyhow::Result<()> { let vertex_buffer = canvas.graphics.upload_verts( canvas.width as _, canvas.height as _, self.rect.x, self.rect.y, self.rect.w, self.rect.h, )?; let clamped_radius = self .corner_radius .min(self.rect.w / 2.0) .min(self.rect.h / 2.0); let skew_radius = [clamped_radius / self.rect.w, clamped_radius / self.rect.h]; let set0 = canvas.pipeline_bg_color.uniform_buffer( 0, vec![ color.x, color.y, color.z, color.w, skew_radius[0], skew_radius[1], ], )?; let pass = canvas.pipeline_bg_color.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer.clone(), canvas.graphics.quad_indices.clone(), vec![set0], )?; cmd_buffer.run_ref(&pass) } pub(super) fn render_text( &self, canvas: &CanvasData, app: &mut AppState, cmd_buffer: &mut WlxCommandBuffer, ) -> anyhow::Result<()> { let mut cur_y = self.rect.y; for line in self.text.lines() { let mut cur_x = self.rect.x; for glyph in app .fc .get_glyphs(line, self.size, canvas.graphics.clone())? { if let Some(tex) = glyph.tex.clone() { let vertex_buffer = canvas.graphics.upload_verts( canvas.width as _, canvas.height as _, cur_x + glyph.left, cur_y - glyph.top, glyph.width, glyph.height, )?; let set0 = canvas.pipeline_fg_glyph.uniform_sampler( 0, ImageView::new_default(tex)?, app.graphics.texture_filtering, )?; let set1 = canvas .pipeline_fg_glyph .uniform_buffer(1, self.fg_color.to_array().to_vec())?; let pass = canvas.pipeline_fg_glyph.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer, canvas.graphics.quad_indices.clone(), vec![set0, set1], )?; cmd_buffer.run_ref(&pass)?; } cur_x += glyph.advance; } cur_y += (self.size as f32) * 1.5; } Ok(()) } pub(super) fn render_text_centered( &self, canvas: &CanvasData, app: &mut AppState, cmd_buffer: &mut WlxCommandBuffer, ) -> anyhow::Result<()> { let (w, h) = app .fc .get_text_size(&self.text, self.size, canvas.graphics.clone())?; let mut cur_y = self.rect.y + (self.rect.h) - (h * 0.5) - (self.size as f32 * 0.25); for line in self.text.lines() { let mut cur_x = self.rect.x + (self.rect.w * 0.5) - (w * 0.5); for glyph in app .fc .get_glyphs(line, self.size, canvas.graphics.clone())? { if let Some(tex) = glyph.tex.clone() { let vertex_buffer = canvas.graphics.upload_verts( canvas.width as _, canvas.height as _, cur_x + glyph.left, cur_y - glyph.top, glyph.width, glyph.height, )?; let set0 = canvas.pipeline_fg_glyph.uniform_sampler( 0, ImageView::new_default(tex)?, app.graphics.texture_filtering, )?; let set1 = canvas .pipeline_fg_glyph .uniform_buffer(1, self.fg_color.to_array().to_vec())?; let pass = canvas.pipeline_fg_glyph.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer, canvas.graphics.quad_indices.clone(), vec![set0, set1], )?; cmd_buffer.run_ref(&pass)?; } cur_x += glyph.advance; } cur_y += (self.size as f32) * 1.5; } Ok(()) } pub(super) fn render_sprite_bg( &self, canvas: &CanvasData, app: &mut AppState, cmd_buffer: &mut WlxCommandBuffer, ) -> anyhow::Result<()> { let Some(view) = self.sprite.as_ref() else { return Ok(()); }; let vertex_buffer = canvas.graphics.upload_verts( canvas.width as _, canvas.height as _, self.rect.x, self.rect.y, self.rect.w, self.rect.h, )?; let set0 = canvas.pipeline_bg_sprite.uniform_sampler( 0, view.clone(), app.graphics.texture_filtering, )?; let uniform = vec![ self.sprite_st.x, self.sprite_st.y, self.sprite_st.z, self.sprite_st.w, self.fg_color.x, self.fg_color.y, self.fg_color.z, self.fg_color.w, ]; let set1 = canvas.pipeline_bg_sprite.uniform_buffer(1, uniform)?; let pass = canvas.pipeline_bg_sprite.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer, canvas.graphics.quad_indices.clone(), vec![set0, set1], )?; cmd_buffer.run_ref(&pass)?; Ok(()) } #[allow(dead_code)] pub(super) fn render_sprite_hl( &self, canvas: &CanvasData, app: &mut AppState, cmd_buffer: &mut WlxCommandBuffer, color: GuiColor, ) -> anyhow::Result<()> { let Some(view) = self.sprite.as_ref() else { return Ok(()); }; let vertex_buffer = canvas.graphics.upload_verts( canvas.width as _, canvas.height as _, self.rect.x, self.rect.y, self.rect.w, self.rect.h, )?; let set0 = canvas.pipeline_hl_sprite.uniform_sampler( 0, view.clone(), app.graphics.texture_filtering, )?; let uniform = vec![ self.sprite_st.x, self.sprite_st.y, self.sprite_st.z, self.sprite_st.w, color.x, color.y, color.z, color.w, ]; let set1 = canvas.pipeline_hl_sprite.uniform_buffer(1, uniform)?; let pass = canvas.pipeline_hl_sprite.create_pass( [canvas.width as _, canvas.height as _], vertex_buffer, canvas.graphics.quad_indices.clone(), vec![set0, set1], )?; cmd_buffer.run_ref(&pass)?; Ok(()) } }