256 lines
7.0 KiB
Rust
256 lines
7.0 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 => THEME.palette.blue,
|
|
DialogResponse::No => THEME.palette.red,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct DialogStyle {
|
|
pub rounding: f32,
|
|
pub header: DialogHeaderStyle,
|
|
pub body: DialogBodyStyle,
|
|
pub footer: DialogFooterStyle,
|
|
}
|
|
|
|
impl Default for DialogStyle {
|
|
fn default() -> Self {
|
|
Self {
|
|
rounding: THEME.metrics.surface_rounding,
|
|
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: THEME.palette.surface,
|
|
height: 20.0,
|
|
text_font: Font::new(crate::DISPLAY_FONT),
|
|
text_color: THEME.palette.text,
|
|
text_scale_factor: 0.65,
|
|
text_baseline: 0.25,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct DialogBodyStyle {
|
|
pub color: Color,
|
|
pub text_font: Font,
|
|
pub text_color: Color,
|
|
pub text_size: f32,
|
|
}
|
|
|
|
impl Default for DialogBodyStyle {
|
|
fn default() -> Self {
|
|
Self {
|
|
color: THEME.palette.base,
|
|
text_font: Font::new(crate::CONTENT_FONT),
|
|
text_color: THEME.palette.text,
|
|
text_size: 5.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct DialogFooterStyle {
|
|
pub icon_font: Font,
|
|
pub button_radius: f32,
|
|
pub color: Color,
|
|
pub button_fg: Color,
|
|
pub height: f32,
|
|
}
|
|
|
|
impl Default for DialogFooterStyle {
|
|
fn default() -> Self {
|
|
Self {
|
|
icon_font: Font::new(crate::ICON_FONT),
|
|
button_radius: 7.5,
|
|
color: THEME.palette.surface,
|
|
button_fg: THEME.palette.white,
|
|
height: 15.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
pub struct DialogInfo {
|
|
pub title: String,
|
|
pub content: String,
|
|
pub responses: Vec<DialogResponse>,
|
|
}
|
|
|
|
pub struct Dialog {
|
|
style: DialogStyle,
|
|
title: Offset<Label>,
|
|
content: Offset<Label>,
|
|
content_size: Vec2,
|
|
buttons: Vec<DialogButton>,
|
|
}
|
|
|
|
impl Dialog {
|
|
pub fn new(style: DialogStyle, info: &DialogInfo) -> Self {
|
|
let mut buttons = Vec::new();
|
|
for response in info.responses.iter() {
|
|
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: style.footer.button_fg,
|
|
};
|
|
|
|
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::ZERO);
|
|
|
|
buttons.push(DialogButton {
|
|
response: *response,
|
|
button,
|
|
});
|
|
}
|
|
|
|
let title_scale = style.header.height * style.header.text_scale_factor;
|
|
let title = Label::new(
|
|
LabelText {
|
|
font: style.header.text_font,
|
|
text: info.title.to_string(),
|
|
},
|
|
HorizontalAlignment::Center,
|
|
title_scale,
|
|
style.header.text_color,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
);
|
|
|
|
let content = Label::new(
|
|
LabelText {
|
|
font: style.body.text_font,
|
|
text: info.content.to_string(),
|
|
},
|
|
HorizontalAlignment::Center,
|
|
style.body.text_size,
|
|
style.body.text_color,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
);
|
|
|
|
let mut dialog = Self {
|
|
style,
|
|
title: Offset::new(title, Vec2::ZERO),
|
|
content: Offset::new(content, Vec2::ZERO),
|
|
content_size: Vec2::ONE,
|
|
buttons,
|
|
};
|
|
|
|
dialog.resize(Vec2::splat(100.0));
|
|
|
|
dialog
|
|
}
|
|
|
|
pub fn resize(&mut self, size: Vec2) {
|
|
let style = &self.style;
|
|
let width = size.x;
|
|
let width2 = width / 2.0;
|
|
let body_height = size.y - style.header.height - style.footer.height;
|
|
let body_height = body_height.max(0.0);
|
|
let title_baseline = style.header.height * (1.0 - style.header.text_baseline);
|
|
let content_baseline = style.header.height + body_height / 2.0;
|
|
let button_baseline = style.header.height + body_height + style.footer.height / 2.0;
|
|
let button_spacing = width / self.buttons.len() as f32;
|
|
let button_spacing2 = button_spacing / 2.0;
|
|
|
|
self.content_size = Vec2::new(width, body_height);
|
|
self.title.set_offset(Vec2::new(width2, title_baseline));
|
|
self.content.set_offset(Vec2::new(width2, content_baseline));
|
|
|
|
for (index, button) in self.buttons.iter_mut().enumerate() {
|
|
let button_x = button_spacing * index as f32 + button_spacing2;
|
|
button
|
|
.button
|
|
.set_offset(Vec2::new(button_x, button_baseline));
|
|
}
|
|
}
|
|
}
|
|
|
|
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 = self.content_size.x;
|
|
let rounding = style.rounding;
|
|
|
|
let header_size = Vec2::new(width, style.header.height);
|
|
let header_rect = Rect::from_xy_size(Vec2::ZERO, header_size);
|
|
let header_corners = CornerFlags::TOP;
|
|
let body_rect = Rect::from_xy_size(header_rect.bl(), self.content_size);
|
|
let footer_size = Vec2::new(width, style.footer.height);
|
|
let footer_rect = Rect::from_xy_size(body_rect.bl(), 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>,
|
|
}
|