use crate::render::{RenderContext, TextAlign, TextBaseline};
use super::super::themes::dialog::{DialogTheme, DialogSize};
pub fn render_dialog_overlay(
ctx: &mut dyn RenderContext,
canvas_width: f64,
canvas_height: f64,
theme: &DialogTheme,
) {
ctx.set_fill_color(theme.overlay_color());
ctx.fill_rect(0.0, 0.0, canvas_width, canvas_height);
}
pub fn render_dialog(
ctx: &mut dyn RenderContext,
canvas_width: f64,
canvas_height: f64,
title: &str,
body: &str,
size: DialogSize,
theme: &DialogTheme,
) -> (f64, f64) {
let dialog_width = theme.width(size);
let padding = theme.padding();
let border_radius = theme.border_radius();
ctx.set_font(theme.title_font());
let _title_width = ctx.measure_text(title);
ctx.set_font(theme.body_font());
let _body_width = ctx.measure_text(body);
let title_height = 22.0;
let body_height = 16.0;
let dialog_height = padding + title_height + body_height + padding;
let dialog_x = (canvas_width - dialog_width) / 2.0;
let dialog_y = (canvas_height - dialog_height) / 2.0;
let (shadow_color, _shadow_blur, shadow_offset_x, shadow_offset_y) = theme.shadow();
ctx.save();
ctx.set_global_alpha(1.0); ctx.set_fill_color(shadow_color);
let shadow_x = dialog_x + shadow_offset_x;
let shadow_y = dialog_y + shadow_offset_y;
for i in 0..3 {
let offset = (i as f64) * 2.0;
ctx.set_global_alpha(0.15 / (i as f64 + 1.0));
ctx.fill_rounded_rect(
shadow_x - offset,
shadow_y - offset,
dialog_width + (offset * 2.0),
dialog_height + (offset * 2.0),
border_radius + offset,
);
}
ctx.restore();
ctx.set_fill_color(theme.bg_color());
ctx.fill_rounded_rect(dialog_x, dialog_y, dialog_width, dialog_height, border_radius);
ctx.set_fill_color(theme.title_color());
ctx.set_font(theme.title_font());
ctx.set_text_align(TextAlign::Left);
ctx.set_text_baseline(TextBaseline::Top);
let title_x = dialog_x + padding;
let title_y = dialog_y + padding;
ctx.fill_text(title, title_x, title_y);
ctx.set_fill_color(theme.body_color());
ctx.set_font(theme.body_font());
ctx.set_text_align(TextAlign::Left);
ctx.set_text_baseline(TextBaseline::Top);
let body_x = dialog_x + padding;
let body_y = title_y + title_height;
ctx.fill_text(body, body_x, body_y);
(dialog_width, dialog_height)
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::super::colors::AppearanceMode;
struct MockContext {
text_width: f64,
}
impl MockContext {
fn new() -> Self {
Self { text_width: 100.0 }
}
}
impl RenderContext for MockContext {
fn dpr(&self) -> f64 { 1.0 }
fn measure_text(&self, _text: &str) -> f64 {
self.text_width
}
fn set_fill_color(&mut self, _color: &str) {}
fn set_stroke_color(&mut self, _color: &str) {}
fn set_stroke_width(&mut self, _width: f64) {}
fn set_line_dash(&mut self, _pattern: &[f64]) {}
fn set_line_cap(&mut self, _cap: &str) {}
fn set_line_join(&mut self, _join: &str) {}
fn set_global_alpha(&mut self, _alpha: f64) {}
fn begin_path(&mut self) {}
fn move_to(&mut self, _x: f64, _y: f64) {}
fn line_to(&mut self, _x: f64, _y: f64) {}
fn close_path(&mut self) {}
fn rect(&mut self, _x: f64, _y: f64, _w: f64, _h: f64) {}
fn arc(&mut self, _cx: f64, _cy: f64, _radius: f64, _start_angle: f64, _end_angle: f64) {}
fn ellipse(&mut self, _cx: f64, _cy: f64, _rx: f64, _ry: f64, _rotation: f64, _start: f64, _end: f64) {}
fn quadratic_curve_to(&mut self, _cpx: f64, _cpy: f64, _x: f64, _y: f64) {}
fn bezier_curve_to(&mut self, _cp1x: f64, _cp1y: f64, _cp2x: f64, _cp2y: f64, _x: f64, _y: f64) {}
fn stroke(&mut self) {}
fn fill(&mut self) {}
fn clip(&mut self) {}
fn stroke_rect(&mut self, _x: f64, _y: f64, _w: f64, _h: f64) {}
fn fill_rect(&mut self, _x: f64, _y: f64, _w: f64, _h: f64) {}
fn set_font(&mut self, _font: &str) {}
fn set_text_align(&mut self, _align: TextAlign) {}
fn set_text_baseline(&mut self, _baseline: TextBaseline) {}
fn fill_text(&mut self, _text: &str, _x: f64, _y: f64) {}
fn stroke_text(&mut self, _text: &str, _x: f64, _y: f64) {}
fn save(&mut self) {}
fn restore(&mut self) {}
fn translate(&mut self, _x: f64, _y: f64) {}
fn rotate(&mut self, _angle: f64) {}
fn scale(&mut self, _x: f64, _y: f64) {}
}
#[test]
fn test_render_dialog_dimensions() {
let mut ctx = MockContext::new();
let theme = DialogTheme::new(AppearanceMode::Light);
let (width, height) = render_dialog(
&mut ctx,
800.0,
600.0,
"Test Dialog",
"This is a test dialog body.",
DialogSize::Regular,
&theme,
);
assert_eq!(width, 448.0);
assert!(height > 0.0);
}
#[test]
fn test_render_dialog_overlay() {
let mut ctx = MockContext::new();
let theme = DialogTheme::new(AppearanceMode::Dark);
render_dialog_overlay(&mut ctx, 800.0, 600.0, &theme);
}
#[test]
fn test_dialog_sizes() {
let mut ctx = MockContext::new();
let theme = DialogTheme::new(AppearanceMode::Light);
let (small_width, _) = render_dialog(
&mut ctx,
800.0,
600.0,
"Small",
"Body",
DialogSize::Small,
&theme,
);
let (regular_width, _) = render_dialog(
&mut ctx,
800.0,
600.0,
"Regular",
"Body",
DialogSize::Regular,
&theme,
);
let (large_width, _) = render_dialog(
&mut ctx,
800.0,
600.0,
"Large",
"Body",
DialogSize::Large,
&theme,
);
assert_eq!(small_width, 320.0);
assert_eq!(regular_width, 448.0);
assert_eq!(large_width, 540.0);
}
}