Add dialog box
This commit is contained in:
parent
ba5b31275d
commit
722013a3c9
|
@ -10,14 +10,14 @@ use canary_script::*;
|
|||
use glam::Vec2;
|
||||
use widgets::Widget;
|
||||
|
||||
export_abi!(DummyPanel);
|
||||
export_abi!(ConfirmationDialogPanel);
|
||||
|
||||
pub struct DummyPanel {
|
||||
pub struct MainMenuPanel {
|
||||
panel: Panel,
|
||||
menu: widgets::MainMenu,
|
||||
}
|
||||
|
||||
impl BindPanel for DummyPanel {
|
||||
impl BindPanel for MainMenuPanel {
|
||||
fn bind(panel: Panel) -> Box<dyn PanelImpl> {
|
||||
Box::new(Self {
|
||||
panel,
|
||||
|
@ -26,7 +26,7 @@ impl BindPanel for DummyPanel {
|
|||
}
|
||||
}
|
||||
|
||||
impl PanelImpl for DummyPanel {
|
||||
impl PanelImpl for MainMenuPanel {
|
||||
fn update(&mut self, dt: f32) {
|
||||
self.menu.update(dt);
|
||||
}
|
||||
|
@ -40,3 +40,33 @@ impl PanelImpl for DummyPanel {
|
|||
self.menu.on_cursor_event(kind, at.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfirmationDialogPanel {
|
||||
panel: Panel,
|
||||
dialog: widgets::dialog::Dialog,
|
||||
}
|
||||
|
||||
impl BindPanel for ConfirmationDialogPanel {
|
||||
fn bind(panel: Panel) -> Box<dyn PanelImpl> {
|
||||
use widgets::dialog::*;
|
||||
let style = DialogStyle::default();
|
||||
let responses = &[DialogResponse::Yes, DialogResponse::No];
|
||||
let dialog = Dialog::new(style, responses);
|
||||
Box::new(Self { panel, dialog })
|
||||
}
|
||||
}
|
||||
|
||||
impl PanelImpl for ConfirmationDialogPanel {
|
||||
fn update(&mut self, dt: f32) {
|
||||
self.dialog.update(dt);
|
||||
}
|
||||
|
||||
fn draw(&mut self) {
|
||||
let ctx = canary_script::draw::DrawContext::new(self.panel);
|
||||
self.dialog.draw(&ctx);
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: canary_script::Vec2) {
|
||||
self.dialog.on_cursor_event(kind, at.into());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,17 @@ pub enum ButtonState {
|
|||
Releasing,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RoundButtonStyle {
|
||||
pub radius: f32,
|
||||
pub spacing: f32,
|
||||
pub thickness: f32,
|
||||
pub color: Color,
|
||||
pub icon_color: Color,
|
||||
}
|
||||
|
||||
pub struct RoundButton {
|
||||
pos: Vec2,
|
||||
radius: f32,
|
||||
spacing: f32,
|
||||
thickness: f32,
|
||||
style: RoundButtonStyle,
|
||||
shrink_anim: Animation<EaseOutQuint>,
|
||||
was_clicked: bool,
|
||||
state: ButtonState,
|
||||
|
@ -26,23 +32,17 @@ pub struct RoundButton {
|
|||
}
|
||||
|
||||
impl RoundButton {
|
||||
pub fn new(icon: Option<LabelText>) -> Self {
|
||||
let pos = Vec2::ZERO;
|
||||
let radius = 0.05;
|
||||
|
||||
pub fn new(style: RoundButtonStyle, icon: Option<LabelText>) -> Self {
|
||||
let icon = icon.map(|text| {
|
||||
let scale = radius * 1.5;
|
||||
let center = pos;
|
||||
let color = Color::BLACK;
|
||||
let scale = style.radius * 1.5;
|
||||
let center = Vec2::ZERO;
|
||||
let color = style.icon_color;
|
||||
|
||||
Icon::new(text, scale, color, center)
|
||||
});
|
||||
|
||||
Self {
|
||||
pos,
|
||||
radius,
|
||||
spacing: 0.01,
|
||||
thickness: 0.002,
|
||||
style,
|
||||
shrink_anim: Animation::new(EaseOutQuint, 0.1, 1.0, 0.0),
|
||||
was_clicked: false,
|
||||
state: ButtonState::Idle,
|
||||
|
@ -64,16 +64,12 @@ impl Widget for RoundButton {
|
|||
}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
let color = Color {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
let spacing = self.shrink_anim.get() * self.spacing;
|
||||
ctx.draw_circle(self.pos, self.radius, color);
|
||||
ctx.draw_ring(self.pos, self.radius + spacing, self.thickness, color);
|
||||
let radius = self.style.radius;
|
||||
let color = self.style.color;
|
||||
let center = Vec2::ZERO;
|
||||
let spacing = self.shrink_anim.get() * self.style.spacing;
|
||||
ctx.draw_circle(center, radius, color);
|
||||
ctx.draw_ring(center, radius + spacing, self.style.thickness, color);
|
||||
|
||||
self.icon.draw(ctx);
|
||||
}
|
||||
|
@ -81,7 +77,7 @@ impl Widget for RoundButton {
|
|||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
match kind {
|
||||
CursorEventKind::Select => {
|
||||
if self.pos.distance(at) < self.radius {
|
||||
if at.length() < self.style.radius {
|
||||
self.shrink_anim.ease_in();
|
||||
self.state = ButtonState::Clicked;
|
||||
}
|
||||
|
@ -145,7 +141,12 @@ impl Button for RectButton {
|
|||
}
|
||||
|
||||
impl RectButton {
|
||||
pub fn new(style: RectButtonStyle, rect: Rect, label: Option<LabelText>, icon: Option<LabelText>) -> Self {
|
||||
pub fn new(
|
||||
style: RectButtonStyle,
|
||||
rect: Rect,
|
||||
label: Option<LabelText>,
|
||||
icon: Option<LabelText>,
|
||||
) -> Self {
|
||||
let mut label_left = rect.bl.x;
|
||||
let mut alignment = HorizontalAlignment::Center;
|
||||
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
use super::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
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_color: Color,
|
||||
pub text_size_scale: f32,
|
||||
}
|
||||
|
||||
impl Default for DialogHeaderStyle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
color: Color::WHITE,
|
||||
height: 0.2,
|
||||
text_color: Color::BLACK,
|
||||
text_size_scale: 0.7,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DialogBodyStyle {
|
||||
pub color: Color,
|
||||
pub height: f32,
|
||||
pub text_color: Color,
|
||||
pub text_size_scale: f32,
|
||||
}
|
||||
|
||||
impl Default for DialogBodyStyle {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
color: Color::new(1., 1., 1., 0.8),
|
||||
height: 0.6,
|
||||
text_color: Color::BLACK,
|
||||
text_size_scale: 0.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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("Iosevka Nerd Font"),
|
||||
button_radius: 0.1,
|
||||
color: Color::WHITE,
|
||||
height: 0.25,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Dialog {
|
||||
style: DialogStyle,
|
||||
buttons: Vec<DialogButton>,
|
||||
}
|
||||
|
||||
impl Dialog {
|
||||
pub fn new(style: DialogStyle, responses: &[DialogResponse]) -> Self {
|
||||
let button_y = -(style.body.height + style.footer.height) / 2.0;
|
||||
let button_spacing = style.width / responses.len() as f32;
|
||||
let button_spacing2 = button_spacing / 2.0;
|
||||
|
||||
let mut buttons = Vec::new();
|
||||
for (index, response) in responses.iter().enumerate() {
|
||||
let button_x = button_spacing * index as f32 + button_spacing2 - style.width / 2.0;
|
||||
|
||||
let radius = style.footer.button_radius;
|
||||
let button_style = RoundButtonStyle {
|
||||
radius: radius * 0.8,
|
||||
spacing: radius * 0.15,
|
||||
thickness: radius * 0.05,
|
||||
color: response.get_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, buttons }
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Dialog {
|
||||
fn update(&mut self, dt: f32) {
|
||||
for button in self.buttons.iter_mut() {
|
||||
button.button.update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
for button in self.buttons.iter_mut() {
|
||||
button.button.draw(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
for button in self.buttons.iter_mut() {
|
||||
button.button.on_cursor_event(kind, at);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DialogButton {
|
||||
response: DialogResponse,
|
||||
button: Offset<RoundButton>,
|
||||
}
|
|
@ -5,12 +5,13 @@ use canary_script::{Font, TextLayout};
|
|||
use keyframe::functions::*;
|
||||
|
||||
pub mod button;
|
||||
pub mod dialog;
|
||||
pub mod menu;
|
||||
pub mod scroll;
|
||||
pub mod shell;
|
||||
pub mod text;
|
||||
|
||||
use button::{Button, RectButton, RectButtonStyle, RoundButton};
|
||||
use button::{Button, RectButton, RectButtonStyle, RoundButton, RoundButtonStyle};
|
||||
use menu::{SlotMenu, SlotMenuEvent, TabMenu};
|
||||
use scroll::{ScrollBar, ScrollView};
|
||||
use shell::{Offset, Reveal};
|
||||
|
@ -63,6 +64,14 @@ impl Default for MainMenu {
|
|||
let icon_font = Font::new("Iosevka Nerd Font");
|
||||
let icons = ["", "", "", "", "", ""];
|
||||
|
||||
let button_style = RoundButtonStyle {
|
||||
radius: 0.05,
|
||||
spacing: 0.01,
|
||||
thickness: 0.002,
|
||||
color: Color::WHITE,
|
||||
icon_color: Color::BLACK,
|
||||
};
|
||||
|
||||
let mut buttons = Vec::new();
|
||||
for icon in icons {
|
||||
let text = LabelText {
|
||||
|
@ -70,7 +79,7 @@ impl Default for MainMenu {
|
|||
text: icon.to_string(),
|
||||
};
|
||||
|
||||
let button = RoundButton::new(Some(text));
|
||||
let button = RoundButton::new(button_style.clone(), Some(text));
|
||||
buttons.push(button);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue