canary-rs/crates/sao-ui-rs/src/widgets/dialog.rs

248 lines
6.5 KiB
Rust

// Copyright (c) 2022 Marceline Cramer
// SPDX-License-Identifier: AGPL-3.0-or-later
use super::prelude::*;
use button::{RoundButton, RoundButtonStyle};
use serde::Deserialize;
use shell::Offset;
use text::{HorizontalAlignment, Label, LabelText};
#[derive(Copy, Clone, Debug, Deserialize)]
pub enum DialogResponse {
Yes,
No,
}
impl DialogResponse {
pub fn get_text(&self) -> &'static str {
match self {
DialogResponse::Yes => "",
DialogResponse::No => "",
}
}
pub fn get_color(&self) -> Color {
match self {
DialogResponse::Yes => Color::BLUE,
DialogResponse::No => Color::RED,
}
}
}
#[derive(Clone)]
pub struct DialogStyle {
pub width: f32,
pub rounding: f32,
pub header: DialogHeaderStyle,
pub body: DialogBodyStyle,
pub footer: DialogFooterStyle,
}
impl Default for DialogStyle {
fn default() -> Self {
Self {
width: 1.2,
rounding: 0.02,
header: Default::default(),
body: Default::default(),
footer: Default::default(),
}
}
}
#[derive(Clone)]
pub struct DialogHeaderStyle {
pub color: Color,
pub height: f32,
pub text_font: Font,
pub text_color: Color,
pub text_scale_factor: f32,
pub text_baseline: f32,
}
impl Default for DialogHeaderStyle {
fn default() -> Self {
Self {
color: Color::WHITE,
height: 0.3,
text_font: Font::new(crate::DISPLAY_FONT),
text_color: Color::BLACK,
text_scale_factor: 0.65,
text_baseline: 0.25,
}
}
}
#[derive(Clone)]
pub struct DialogBodyStyle {
pub color: Color,
pub height: f32,
pub text_font: Font,
pub text_color: Color,
pub text_scale_factor: f32,
}
impl Default for DialogBodyStyle {
fn default() -> Self {
Self {
color: Color::new(1., 1., 1., 0.8),
height: 0.6,
text_font: Font::new(crate::CONTENT_FONT),
text_color: Color::BLACK,
text_scale_factor: 0.15,
}
}
}
#[derive(Clone)]
pub struct DialogFooterStyle {
pub icon_font: Font,
pub button_radius: f32,
pub color: Color,
pub height: f32,
}
impl Default for DialogFooterStyle {
fn default() -> Self {
Self {
icon_font: Font::new(crate::ICON_FONT),
button_radius: 0.1,
color: Color::WHITE,
height: 0.25,
}
}
}
#[derive(Deserialize)]
pub struct DialogInfo {
pub title: String,
pub content: String,
pub responses: Vec<DialogResponse>,
}
pub struct Dialog {
style: DialogStyle,
title: Label,
content: Label,
buttons: Vec<DialogButton>,
}
impl Dialog {
pub fn new(style: DialogStyle, info: &DialogInfo) -> Self {
let width2 = style.width / 2.0;
let button_y = -(style.body.height + style.footer.height) / 2.0;
let button_spacing = style.width / info.responses.len() as f32;
let button_spacing2 = button_spacing / 2.0;
let title_scale = style.header.height * style.header.text_scale_factor;
let title_baseline =
style.header.height * style.header.text_baseline + style.body.height / 2.0;
let title = Label::new(
LabelText {
font: style.header.text_font,
text: info.title.to_string(),
},
HorizontalAlignment::Center,
title_scale,
style.header.text_color,
-width2,
width2,
title_baseline,
);
let content_scale = style.body.height * style.body.text_scale_factor;
let content = Label::new(
LabelText {
font: style.body.text_font,
text: info.content.to_string(),
},
HorizontalAlignment::Center,
content_scale,
style.body.text_color,
-width2,
width2,
0.0,
);
let mut buttons = Vec::new();
for (index, response) in info.responses.iter().enumerate() {
let button_x = button_spacing * index as f32 + button_spacing2 - style.width / 2.0;
let color = response.get_color();
let radius = style.footer.button_radius;
let button_style = RoundButtonStyle {
radius: radius * 0.8,
spacing: radius * 0.15,
thickness: radius * 0.05,
body_color: color,
ring_color: color,
icon_color: Color::WHITE,
};
let text = LabelText {
font: style.footer.icon_font,
text: response.get_text().to_string(),
};
let button = RoundButton::new(button_style, Some(text));
let button = Offset::new(button, Vec2::new(button_x, button_y));
buttons.push(DialogButton {
response: *response,
button,
});
}
Self {
style,
title,
content,
buttons,
}
}
}
impl Container for Dialog {
fn with_children(&mut self, mut f: impl FnMut(&mut dyn Widget)) {
f(&mut self.title);
f(&mut self.content);
for button in self.buttons.iter_mut() {
f(&mut button.button);
}
}
fn draw(&mut self, ctx: &DrawContext) {
let style = &self.style;
let width = style.width;
let width2 = width / 2.0;
let rounding = style.rounding;
let header_xy = Vec2::new(-width2, style.body.height / 2.0);
let header_size = Vec2::new(width, style.header.height);
let header_rect = Rect::from_xy_size(header_xy, header_size);
let header_corners = CornerFlags::TOP;
let body_tr = Vec2::new(width2, style.body.height / 2.0);
let body_rect = Rect {
bl: -body_tr,
tr: body_tr,
};
let footer_xy = Vec2::new(-width2, -style.body.height / 2.0 - style.footer.height);
let footer_size = Vec2::new(width, style.footer.height);
let footer_rect = Rect::from_xy_size(footer_xy, footer_size);
let footer_corners = CornerFlags::BOTTOM;
ctx.draw_rect(body_rect, style.body.color);
ctx.draw_partially_rounded_rect(header_corners, header_rect, rounding, style.header.color);
ctx.draw_partially_rounded_rect(footer_corners, footer_rect, rounding, style.footer.color);
}
}
pub struct DialogButton {
response: DialogResponse,
button: Offset<RoundButton>,
}