use std::rc::Rc;
use rs_math3d::{Color4b, Recti, Vec2i};
use crate::atlas::{AtlasHandle, FontId, IconId, SlotId};
use crate::canvas::Vertex;
use crate::container::Command;
use crate::draw_context::DrawCtx;
use crate::graphics::Graphics;
use crate::input::{Clip, ControlColor, ControlState, InputSnapshot, WidgetOption};
use crate::style::{Color, Image, Style};
use crate::widget::WidgetId;
pub struct WidgetCtx<'a> {
id: WidgetId,
rect: Recti,
draw: DrawCtx<'a>,
focus: &'a mut Option<WidgetId>,
updated_focus: &'a mut bool,
in_hover_root: bool,
input: Option<Rc<InputSnapshot>>,
default_input: InputSnapshot,
}
impl<'a> WidgetCtx<'a> {
fn localize_input(rect: Recti, input: Option<Rc<InputSnapshot>>) -> Option<Rc<InputSnapshot>> {
input.map(|input| {
let mut localized = input.as_ref().clone();
localized.mouse_pos = localized.mouse_pos - Vec2i::new(rect.x, rect.y);
Rc::new(localized)
})
}
fn local_rect_for(&self, rect: Recti) -> Recti {
Recti::new(rect.x - self.rect.x, rect.y - self.rect.y, rect.width, rect.height)
}
fn local_pos_for(&self, pos: Vec2i) -> Vec2i {
pos - Vec2i::new(self.rect.x, self.rect.y)
}
pub(crate) fn new(
id: WidgetId,
rect: Recti,
commands: &'a mut Vec<Command>,
triangle_vertices: &'a mut Vec<Vertex>,
clip_stack: &'a mut Vec<Recti>,
style: &'a Style,
atlas: &'a AtlasHandle,
focus: &'a mut Option<WidgetId>,
updated_focus: &'a mut bool,
in_hover_root: bool,
input: Option<Rc<InputSnapshot>>,
) -> Self {
Self {
id,
rect,
draw: DrawCtx::new(commands, triangle_vertices, clip_stack, style, atlas),
focus,
updated_focus,
in_hover_root,
input: Self::localize_input(rect, input),
default_input: InputSnapshot::default(),
}
}
pub fn id(&self) -> WidgetId {
self.id
}
pub fn rect(&self) -> Recti {
self.rect
}
pub fn input(&self) -> Option<&InputSnapshot> {
self.input.as_deref()
}
pub(crate) fn input_or_default(&self) -> &InputSnapshot {
self.input.as_deref().unwrap_or(&self.default_input)
}
pub fn set_focus(&mut self) {
*self.focus = Some(self.id);
*self.updated_focus = true;
}
pub fn clear_focus(&mut self) {
*self.focus = None;
*self.updated_focus = true;
}
pub fn push_clip_rect(&mut self, rect: Recti) {
self.draw.push_clip_rect(rect);
}
pub fn pop_clip_rect(&mut self) {
self.draw.pop_clip_rect();
}
pub fn with_clip<F: FnOnce(&mut Self)>(&mut self, rect: Recti, f: F) {
self.push_clip_rect(rect);
f(self);
self.pop_clip_rect();
}
pub fn graphics<F>(&mut self, f: F)
where
F: FnOnce(&mut Graphics<'_, 'a>),
{
let mut graphics = self.begin_graphics();
f(&mut graphics);
}
pub fn begin_graphics(&mut self) -> Graphics<'_, 'a> {
Graphics::new(&mut self.draw, self.rect)
}
fn begin_widget_paint(&mut self) -> Graphics<'_, 'a> {
let clip_root = self.draw.current_clip_rect();
Graphics::new_with_clip_root(&mut self.draw, self.rect, clip_root)
}
fn current_clip_rect(&self) -> Recti {
self.draw.current_clip_rect()
}
pub(crate) fn style(&self) -> &Style {
self.draw.style()
}
pub(crate) fn atlas(&self) -> &AtlasHandle {
self.draw.atlas()
}
pub fn set_clip(&mut self, rect: Recti) {
self.draw.set_clip(rect);
}
pub fn check_clip(&self, r: Recti) -> Clip {
self.draw.check_clip(r)
}
pub(crate) fn draw_rect(&mut self, rect: Recti, color: Color) {
let rect = self.local_rect_for(rect);
let mut graphics = self.begin_widget_paint();
graphics.draw_rect(rect, color);
}
pub fn draw_box(&mut self, r: Recti, color: Color) {
let rect = self.local_rect_for(r);
let mut graphics = self.begin_widget_paint();
graphics.draw_box(rect, color);
}
pub(crate) fn draw_text(&mut self, font: FontId, text: &str, pos: Vec2i, color: Color) {
let pos = self.local_pos_for(pos);
let mut graphics = self.begin_widget_paint();
graphics.draw_text(font, text, pos, color);
}
pub(crate) fn draw_icon(&mut self, id: IconId, rect: Recti, color: Color) {
let rect = self.local_rect_for(rect);
let mut graphics = self.begin_widget_paint();
graphics.draw_icon(id, rect, color);
}
pub(crate) fn push_image(&mut self, image: Image, rect: Recti, color: Color) {
let rect = self.local_rect_for(rect);
let mut graphics = self.begin_widget_paint();
graphics.draw_image(image, rect, color);
}
pub(crate) fn draw_slot_with_function(&mut self, id: SlotId, rect: Recti, color: Color, f: Rc<dyn Fn(usize, usize) -> Color4b>) {
let rect = self.local_rect_for(rect);
let mut graphics = self.begin_widget_paint();
graphics.draw_slot_with_function(id, rect, color, f);
}
pub(crate) fn draw_frame(&mut self, rect: Recti, colorid: ControlColor) {
let rect = self.local_rect_for(rect);
let mut graphics = self.begin_widget_paint();
graphics.draw_frame(rect, colorid);
}
pub(crate) fn draw_widget_frame(&mut self, control: &ControlState, rect: Recti, colorid: ControlColor, opt: WidgetOption) {
let rect = self.local_rect_for(rect);
let mut graphics = self.begin_widget_paint();
graphics.draw_widget_frame(control.focused, control.hovered, rect, colorid, opt);
}
pub(crate) fn draw_control_text_with_font(&mut self, font: FontId, text: &str, rect: Recti, colorid: ControlColor, opt: WidgetOption) {
let rect = self.local_rect_for(rect);
let mut graphics = self.begin_widget_paint();
graphics.draw_control_text_with_font(font, text, rect, colorid, opt);
}
pub(crate) fn mouse_over(&self, rect: Recti) -> bool {
let input = match self.input.as_ref() {
Some(input) => input,
None => return false,
};
if !self.in_hover_root {
return false;
}
let local_rect = self.local_rect_for(rect);
let clip_rect = self.local_rect_for(self.current_clip_rect());
local_rect.contains(&input.mouse_pos) && clip_rect.contains(&input.mouse_pos)
}
}