Upgrade SAO theming #43
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
|
||||||
use canary::{DrawCommand, Vec2, PX_PER_MM};
|
use canary::{DrawCommand, Vec2, PX_PER_MM};
|
||||||
use glium::Surface;
|
use glium::{program::ProgramCreationInput, Surface};
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
|
@ -59,9 +59,21 @@ pub struct Graphics {
|
||||||
|
|
||||||
impl Graphics {
|
impl Graphics {
|
||||||
pub fn new(display: glium::Display) -> Self {
|
pub fn new(display: glium::Display) -> Self {
|
||||||
let program =
|
let program = glium::Program::new(
|
||||||
glium::Program::from_source(&display, VERTEX_SHADER_SRC, FRAGMENT_SHADER_SRC, None)
|
&display,
|
||||||
.unwrap();
|
ProgramCreationInput::SourceCode {
|
||||||
|
vertex_shader: VERTEX_SHADER_SRC,
|
||||||
|
tessellation_control_shader: None,
|
||||||
|
tessellation_evaluation_shader: None,
|
||||||
|
geometry_shader: None,
|
||||||
|
fragment_shader: FRAGMENT_SHADER_SRC,
|
||||||
|
transform_feedback_varyings: None,
|
||||||
|
outputs_srgb: true, // don't automatically apply gamma correction
|
||||||
|
uses_point_size: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
Self { display, program }
|
Self { display, program }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ struct App {
|
||||||
last_update: Instant,
|
last_update: Instant,
|
||||||
protocol_buf: String,
|
protocol_buf: String,
|
||||||
bind_message_buf: String,
|
bind_message_buf: String,
|
||||||
|
panel_bg: egui::Color32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
@ -49,6 +50,7 @@ impl App {
|
||||||
last_update: Instant::now(),
|
last_update: Instant::now(),
|
||||||
protocol_buf: String::new(),
|
protocol_buf: String::new(),
|
||||||
bind_message_buf: String::new(),
|
bind_message_buf: String::new(),
|
||||||
|
panel_bg: egui::Color32::TRANSPARENT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +60,8 @@ impl eframe::App for App {
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
|
|
||||||
egui::SidePanel::left("left_panel").show(ctx, |ui| {
|
egui::SidePanel::left("left_panel").show(ctx, |ui| {
|
||||||
|
ui.heading("New Panel");
|
||||||
|
|
||||||
ui.label("Protocol name:");
|
ui.label("Protocol name:");
|
||||||
ui.text_edit_singleline(&mut self.protocol_buf);
|
ui.text_edit_singleline(&mut self.protocol_buf);
|
||||||
|
|
||||||
|
@ -81,6 +85,14 @@ impl eframe::App for App {
|
||||||
|
|
||||||
self.panels.push(panel);
|
self.panels.push(panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui.separator();
|
||||||
|
ui.heading("Global Settings");
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.label("Panel background color: ");
|
||||||
|
ui.color_edit_button_srgba(&mut self.panel_bg);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let dt = self.last_update.elapsed().as_secs_f32();
|
let dt = self.last_update.elapsed().as_secs_f32();
|
||||||
|
@ -88,7 +100,7 @@ impl eframe::App for App {
|
||||||
|
|
||||||
for panel in self.panels.iter_mut() {
|
for panel in self.panels.iter_mut() {
|
||||||
panel.panel.update(dt);
|
panel.panel.update(dt);
|
||||||
panel.show(ctx);
|
panel.show(self.panel_bg, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,83 +114,87 @@ pub struct PanelWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PanelWindow {
|
impl PanelWindow {
|
||||||
pub fn show(&mut self, ctx: &egui::Context) {
|
pub fn show(&mut self, bg: egui::Color32, ctx: &egui::Context) {
|
||||||
|
let frame = egui::Frame::window(&ctx.style()).fill(bg);
|
||||||
let window_id = egui::Id::new(format!("panel_{}", self.index));
|
let window_id = egui::Id::new(format!("panel_{}", self.index));
|
||||||
egui::Window::new("Panel").id(window_id).show(ctx, |ui| {
|
egui::Window::new("Panel")
|
||||||
egui::menu::bar(ui, |ui| {
|
.frame(frame)
|
||||||
ui.checkbox(&mut self.show_msg, "Show Message Editor");
|
.id(window_id)
|
||||||
});
|
.show(ctx, |ui| {
|
||||||
|
egui::menu::bar(ui, |ui| {
|
||||||
|
ui.checkbox(&mut self.show_msg, "Show Message Editor");
|
||||||
|
});
|
||||||
|
|
||||||
let sense = egui::Sense {
|
let sense = egui::Sense {
|
||||||
click: true,
|
click: true,
|
||||||
drag: true,
|
drag: true,
|
||||||
focusable: true,
|
focusable: true,
|
||||||
};
|
|
||||||
|
|
||||||
let desired_size = ui.available_size();
|
|
||||||
let response = ui.allocate_response(desired_size, sense);
|
|
||||||
let rect = response.rect;
|
|
||||||
|
|
||||||
if rect.size() != self.current_size {
|
|
||||||
let size = rect.size();
|
|
||||||
self.current_size = size;
|
|
||||||
|
|
||||||
let size = canary::Vec2::new(size.x, size.y);
|
|
||||||
self.panel.on_resize(size * PX_PER_MM);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(hover_pos) = response.hover_pos() {
|
|
||||||
let local = (hover_pos - rect.left_top()) * PX_PER_MM;
|
|
||||||
let pos = canary::Vec2::new(local.x, local.y);
|
|
||||||
|
|
||||||
let kind = if response.drag_started() {
|
|
||||||
CursorEventKind::Select
|
|
||||||
} else if response.drag_released() {
|
|
||||||
CursorEventKind::Deselect
|
|
||||||
} else if response.dragged() {
|
|
||||||
CursorEventKind::Drag
|
|
||||||
} else {
|
|
||||||
CursorEventKind::Hover
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.panel.on_cursor_event(kind, pos);
|
let desired_size = ui.available_size();
|
||||||
}
|
let response = ui.allocate_response(desired_size, sense);
|
||||||
|
let rect = response.rect;
|
||||||
|
|
||||||
let texture = egui::TextureId::Managed(0);
|
if rect.size() != self.current_size {
|
||||||
let uv = egui::pos2(0.0, 0.0);
|
let size = rect.size();
|
||||||
let mut mesh = egui::Mesh::with_texture(texture);
|
self.current_size = size;
|
||||||
|
|
||||||
let commands = self.panel.draw();
|
let size = canary::Vec2::new(size.x, size.y);
|
||||||
for command in commands.into_iter() {
|
self.panel.on_resize(size * PX_PER_MM);
|
||||||
let voff = mesh.vertices.len() as u32;
|
|
||||||
|
|
||||||
match command {
|
|
||||||
canary::DrawCommand::Mesh { vertices, indices } => {
|
|
||||||
for v in vertices.iter() {
|
|
||||||
use egui::epaint::Vertex;
|
|
||||||
let pos = v.position / PX_PER_MM;
|
|
||||||
let pos = egui::pos2(pos.x, pos.y);
|
|
||||||
let pos = pos + rect.left_top().to_vec2();
|
|
||||||
let (r, g, b, a) = v.color.to_rgba_unmultiplied();
|
|
||||||
let color = egui::Color32::from_rgba_unmultiplied(r, g, b, a);
|
|
||||||
let v = Vertex { pos, uv, color };
|
|
||||||
mesh.vertices.push(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in indices.iter() {
|
|
||||||
mesh.indices.push(i + voff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let painter = ui.painter_at(rect);
|
if let Some(hover_pos) = response.hover_pos() {
|
||||||
let shape = egui::Shape::mesh(mesh);
|
let local = (hover_pos - rect.left_top()) * PX_PER_MM;
|
||||||
painter.add(shape);
|
let pos = canary::Vec2::new(local.x, local.y);
|
||||||
|
|
||||||
response
|
let kind = if response.drag_started() {
|
||||||
});
|
CursorEventKind::Select
|
||||||
|
} else if response.drag_released() {
|
||||||
|
CursorEventKind::Deselect
|
||||||
|
} else if response.dragged() {
|
||||||
|
CursorEventKind::Drag
|
||||||
|
} else {
|
||||||
|
CursorEventKind::Hover
|
||||||
|
};
|
||||||
|
|
||||||
|
self.panel.on_cursor_event(kind, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
let texture = egui::TextureId::Managed(0);
|
||||||
|
let uv = egui::pos2(0.0, 0.0);
|
||||||
|
let mut mesh = egui::Mesh::with_texture(texture);
|
||||||
|
|
||||||
|
let commands = self.panel.draw();
|
||||||
|
for command in commands.into_iter() {
|
||||||
|
let voff = mesh.vertices.len() as u32;
|
||||||
|
|
||||||
|
match command {
|
||||||
|
canary::DrawCommand::Mesh { vertices, indices } => {
|
||||||
|
for v in vertices.iter() {
|
||||||
|
use egui::epaint::Vertex;
|
||||||
|
let pos = v.position / PX_PER_MM;
|
||||||
|
let pos = egui::pos2(pos.x, pos.y);
|
||||||
|
let pos = pos + rect.left_top().to_vec2();
|
||||||
|
let (r, g, b, a) = v.color.to_rgba_unmultiplied();
|
||||||
|
let color = egui::Color32::from_rgba_unmultiplied(r, g, b, a);
|
||||||
|
let v = Vertex { pos, uv, color };
|
||||||
|
mesh.vertices.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in indices.iter() {
|
||||||
|
mesh.indices.push(i + voff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let painter = ui.painter_at(rect);
|
||||||
|
let shape = egui::Shape::mesh(mesh);
|
||||||
|
painter.add(shape);
|
||||||
|
|
||||||
|
response
|
||||||
|
});
|
||||||
|
|
||||||
let msg_edit_id = egui::Id::new(format!("msg_edit_{}", self.index));
|
let msg_edit_id = egui::Id::new(format!("msg_edit_{}", self.index));
|
||||||
egui::Window::new("Message Editor")
|
egui::Window::new("Message Editor")
|
||||||
|
|
|
@ -173,13 +173,13 @@ impl Color {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn alpha_multiply(&self, mul: u8) -> Self {
|
pub const fn alpha_multiply(&self, mul: u8) -> Self {
|
||||||
let a = self.0 as u8 as u16;
|
let a = self.0 as u8 as u16;
|
||||||
let multiplied = ((a * (mul as u16)) >> 8) as u8;
|
let multiplied = ((a * (mul as u16)) >> 8) as u8;
|
||||||
self.with_alpha(multiplied)
|
self.with_alpha(multiplied)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_alpha(&self, alpha: u8) -> Self {
|
pub const fn with_alpha(&self, alpha: u8) -> Self {
|
||||||
Self(self.0 & 0xffffff00 | alpha as u32)
|
Self(self.0 & 0xffffff00 | alpha as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
pub mod anim;
|
pub mod anim;
|
||||||
pub mod main_menu;
|
pub mod main_menu;
|
||||||
pub mod music_player;
|
pub mod music_player;
|
||||||
|
pub mod style;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
use api::*;
|
use api::*;
|
||||||
|
@ -23,6 +24,7 @@ fn bind_panel_impl(panel: Panel, protocol: Message, msg: Message) -> Box<dyn Pan
|
||||||
|
|
||||||
match protocol.as_str() {
|
match protocol.as_str() {
|
||||||
"tebibyte-media.desktop.music-player-controller" => MusicPlayerPanel::bind(panel, msg),
|
"tebibyte-media.desktop.music-player-controller" => MusicPlayerPanel::bind(panel, msg),
|
||||||
|
"wip-dialog" => ConfirmationDialogPanel::bind(panel, msg),
|
||||||
_ => MainMenuPanel::bind(panel, msg),
|
_ => MainMenuPanel::bind(panel, msg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,15 +52,23 @@ impl PanelImpl for ConfirmationDialogPanel {
|
||||||
self.dialog.on_cursor_event(kind, at.into());
|
self.dialog.on_cursor_event(kind, at.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_resize(&mut self, _size: Vec2) {}
|
fn on_resize(&mut self, size: Vec2) {
|
||||||
|
self.dialog.resize(size);
|
||||||
|
}
|
||||||
|
|
||||||
fn on_message(&mut self, _msg: Message) {}
|
fn on_message(&mut self, _msg: Message) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfirmationDialogPanel {
|
impl ConfirmationDialogPanel {
|
||||||
pub fn bind(panel: Panel, msg: Message) -> Box<dyn PanelImpl> {
|
pub fn bind(panel: Panel, msg: Message) -> Box<dyn PanelImpl> {
|
||||||
let msg = msg.to_vec();
|
// let msg = msg.to_vec();
|
||||||
let info: DialogInfo = serde_json::from_slice(&msg).unwrap();
|
// let info: DialogInfo = serde_json::from_slice(&msg).unwrap();
|
||||||
|
|
||||||
|
let info = DialogInfo {
|
||||||
|
title: "Hello world!".to_string(),
|
||||||
|
content: "Testing, testing...".to_string(),
|
||||||
|
responses: vec![DialogResponse::Yes, DialogResponse::No],
|
||||||
|
};
|
||||||
|
|
||||||
use widgets::dialog::*;
|
use widgets::dialog::*;
|
||||||
let style = DialogStyle::default();
|
let style = DialogStyle::default();
|
||||||
|
|
|
@ -64,9 +64,9 @@ impl Default for MainMenu {
|
||||||
radius: 7.5,
|
radius: 7.5,
|
||||||
spacing: 1.5,
|
spacing: 1.5,
|
||||||
thickness: 0.4,
|
thickness: 0.4,
|
||||||
body_color: Color::WHITE,
|
body_color: THEME.palette.surface,
|
||||||
ring_color: Color::WHITE,
|
ring_color: THEME.palette.surface,
|
||||||
icon_color: Color::BLACK,
|
icon_color: THEME.palette.text,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buttons = Vec::new();
|
let mut buttons = Vec::new();
|
||||||
|
@ -150,6 +150,7 @@ pub struct PlayerInfo {
|
||||||
width: f32,
|
width: f32,
|
||||||
height: f32,
|
height: f32,
|
||||||
rounding: f32,
|
rounding: f32,
|
||||||
|
color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlayerInfo {
|
impl PlayerInfo {
|
||||||
|
@ -158,6 +159,7 @@ impl PlayerInfo {
|
||||||
width: 70.0,
|
width: 70.0,
|
||||||
height: 120.0,
|
height: 120.0,
|
||||||
rounding: 5.0,
|
rounding: 5.0,
|
||||||
|
color: THEME.palette.surface,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,7 +172,7 @@ impl RectBounds for PlayerInfo {
|
||||||
|
|
||||||
impl Widget for PlayerInfo {
|
impl Widget for PlayerInfo {
|
||||||
fn draw(&mut self, ctx: &DrawContext) {
|
fn draw(&mut self, ctx: &DrawContext) {
|
||||||
ctx.draw_rounded_rect(self.get_bounds(), self.rounding, Color::WHITE);
|
ctx.draw_rounded_rect(self.get_bounds(), self.rounding, self.color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,8 @@ impl MusicPlayerWidget {
|
||||||
text: content.to_string(),
|
text: content.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let label = Label::new_centered(text, 10.0, Color::BLACK);
|
let color = style.body.text_color;
|
||||||
|
let label = Label::new_centered(text, 10.0, color);
|
||||||
Offset::new(label, Vec2::ZERO)
|
Offset::new(label, Vec2::ZERO)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -196,7 +197,7 @@ impl MusicPlayerWidget {
|
||||||
text,
|
text,
|
||||||
HorizontalAlignment::Center,
|
HorizontalAlignment::Center,
|
||||||
scale,
|
scale,
|
||||||
Color::BLACK,
|
THEME.palette.text,
|
||||||
0.0,
|
0.0,
|
||||||
0.0,
|
0.0,
|
||||||
baseline,
|
baseline,
|
||||||
|
@ -225,18 +226,18 @@ impl MusicPlayerWidget {
|
||||||
radius: style.footer.height * 0.3,
|
radius: style.footer.height * 0.3,
|
||||||
spacing: style.footer.height * 0.1,
|
spacing: style.footer.height * 0.1,
|
||||||
thickness: style.footer.height * 0.025,
|
thickness: style.footer.height * 0.025,
|
||||||
body_color: Color(0xf1b841ff),
|
body_color: THEME.palette.yellow,
|
||||||
ring_color: Color(0xf1b841ff),
|
ring_color: THEME.palette.yellow,
|
||||||
icon_color: Color::BLACK,
|
icon_color: THEME.palette.black,
|
||||||
};
|
};
|
||||||
|
|
||||||
let secondary_button = RoundButtonStyle {
|
let secondary_button = RoundButtonStyle {
|
||||||
radius: style.footer.height * 0.25,
|
radius: style.footer.height * 0.25,
|
||||||
spacing: style.footer.height * 0.05,
|
spacing: style.footer.height * 0.05,
|
||||||
thickness: style.footer.height * 0.025,
|
thickness: style.footer.height * 0.025,
|
||||||
body_color: Color::WHITE,
|
body_color: style.footer.color,
|
||||||
ring_color: Color::BLACK,
|
ring_color: THEME.palette.black,
|
||||||
icon_color: Color::BLACK,
|
icon_color: THEME.palette.black,
|
||||||
};
|
};
|
||||||
|
|
||||||
let prev = RoundButton::new(secondary_button.clone(), Some(prev_text));
|
let prev = RoundButton::new(secondary_button.clone(), Some(prev_text));
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
use canary_script::Color;
|
||||||
|
|
||||||
|
/// A reusable set of colors. Used by default widget styles.
|
||||||
|
pub struct Palette {
|
||||||
|
pub base: Color,
|
||||||
|
pub base_hover: Color,
|
||||||
|
pub base_active: Color,
|
||||||
|
pub surface: Color,
|
||||||
|
pub overlay: Color,
|
||||||
|
pub text: Color,
|
||||||
|
pub black: Color,
|
||||||
|
pub red: Color,
|
||||||
|
pub green: Color,
|
||||||
|
pub yellow: Color,
|
||||||
|
pub blue: Color,
|
||||||
|
pub magenta: Color,
|
||||||
|
pub cyan: Color,
|
||||||
|
pub white: Color,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sword Art Online color palette.
|
||||||
|
pub const SAO_PALETTE: Palette = Palette {
|
||||||
|
base: Color::WHITE.with_alpha(0xa0),
|
||||||
|
base_hover: Color::WHITE.with_alpha(0xd0),
|
||||||
|
base_active: Color::YELLOW,
|
||||||
|
surface: Color::WHITE,
|
||||||
|
overlay: Color::WHITE,
|
||||||
|
text: Color::BLACK,
|
||||||
|
black: Color::BLACK,
|
||||||
|
red: Color::RED,
|
||||||
|
green: Color::GREEN,
|
||||||
|
yellow: Color::YELLOW,
|
||||||
|
blue: Color::BLUE,
|
||||||
|
magenta: Color::MAGENTA,
|
||||||
|
cyan: Color::CYAN,
|
||||||
|
white: Color::WHITE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Rose Pine color palette.
|
||||||
|
pub const ROSE_PINE_PALETTE: Palette = Palette {
|
||||||
|
base: Color(0x191724ff),
|
||||||
|
base_hover: Color(0x21202eff), // Highlight Low
|
||||||
|
base_active: Color(0x403d52ff), // Highlight Med
|
||||||
|
surface: Color(0x1f1d2eff),
|
||||||
|
overlay: Color(0x26233aff),
|
||||||
|
text: Color(0xe0def4ff),
|
||||||
|
black: Color(0x6e6a86ff), // Muted
|
||||||
|
red: Color(0xeb6f92ff), // Love
|
||||||
|
green: Color(0x7fb59fff), // ??? (not in Rose Pine?)
|
||||||
|
yellow: Color(0xf6c177ff), // Gold
|
||||||
|
blue: Color(0x31748fff), // Pine
|
||||||
|
magenta: Color(0xc4a7e7ff), // Iris
|
||||||
|
cyan: Color(0x9ccfd8ff), // Foam
|
||||||
|
white: Color(0xe0def4ff), // Text
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Rose Pine Moon color palette.
|
||||||
|
pub const ROSE_PINE_MOON_PALETTE: Palette = Palette {
|
||||||
|
base: Color(0x232136ff),
|
||||||
|
base_hover: Color(0x2a283eff), // Highlight Low
|
||||||
|
base_active: Color(0x44415aff), // Highlight Med
|
||||||
|
surface: Color(0x2a273fff),
|
||||||
|
overlay: Color(0x393552),
|
||||||
|
text: Color(0xe0def4ff),
|
||||||
|
black: Color(0x6e6a86ff), // Muted
|
||||||
|
red: Color(0xeb6f92ff), // Love
|
||||||
|
green: Color(0x7fb59fff), // ??? (not in Rose Pine?)
|
||||||
|
yellow: Color(0xf6c177ff), // Gold
|
||||||
|
blue: Color(0x3e8fb0ff), // Pine
|
||||||
|
magenta: Color(0xc4a7e7ff), // Iris
|
||||||
|
cyan: Color(0x9ccfd8ff), // Foam
|
||||||
|
white: Color(0xe0def4ff), // Text
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Common measurements for widget shapes.
|
||||||
|
pub struct Metrics {
|
||||||
|
pub surface_rounding: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Common default parameters for widget styles.
|
||||||
|
pub struct Theme {
|
||||||
|
pub palette: Palette,
|
||||||
|
pub metrics: Metrics,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The global theme.
|
||||||
|
pub const THEME: Theme = Theme {
|
||||||
|
palette: ROSE_PINE_MOON_PALETTE,
|
||||||
|
metrics: Metrics {
|
||||||
|
surface_rounding: 5.0,
|
||||||
|
},
|
||||||
|
};
|
|
@ -118,6 +118,8 @@ pub struct RectButtonStyle {
|
||||||
pub inactive_color: Color,
|
pub inactive_color: Color,
|
||||||
pub hover_color: Color,
|
pub hover_color: Color,
|
||||||
pub selected_color: Color,
|
pub selected_color: Color,
|
||||||
|
pub icon_color: Color,
|
||||||
|
pub label_color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for RectButtonStyle {
|
impl Default for RectButtonStyle {
|
||||||
|
@ -129,9 +131,11 @@ impl Default for RectButtonStyle {
|
||||||
label_baseline: 0.25,
|
label_baseline: 0.25,
|
||||||
icon_scale_factor: 0.8,
|
icon_scale_factor: 0.8,
|
||||||
icon_margin_factor: 1.1,
|
icon_margin_factor: 1.1,
|
||||||
inactive_color: Color::WHITE.with_alpha(0x40),
|
inactive_color: THEME.palette.base,
|
||||||
hover_color: Color::WHITE.with_alpha(0xb0),
|
hover_color: THEME.palette.base_hover,
|
||||||
selected_color: Color::YELLOW,
|
selected_color: THEME.palette.base_active,
|
||||||
|
icon_color: THEME.palette.black,
|
||||||
|
label_color: THEME.palette.text,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,7 +172,7 @@ impl RectButton {
|
||||||
label_left += margin;
|
label_left += margin;
|
||||||
alignment = HorizontalAlignment::Left;
|
alignment = HorizontalAlignment::Left;
|
||||||
let scale = rect.height() * style.icon_scale_factor;
|
let scale = rect.height() * style.icon_scale_factor;
|
||||||
let color = Color::BLACK;
|
let color = style.icon_color;
|
||||||
let cx = rect.tl.x + margin / 2.0;
|
let cx = rect.tl.x + margin / 2.0;
|
||||||
let cy = rect.tl.y + rect.height() / 2.0;
|
let cy = rect.tl.y + rect.height() / 2.0;
|
||||||
let center = Vec2::new(cx, cy);
|
let center = Vec2::new(cx, cy);
|
||||||
|
@ -182,7 +186,7 @@ impl RectButton {
|
||||||
let right = rect.br.x;
|
let right = rect.br.x;
|
||||||
let baseline = rect.tl.y;
|
let baseline = rect.tl.y;
|
||||||
let baseline = (rect.height() * (1.0 - style.label_baseline)) + baseline;
|
let baseline = (rect.height() * (1.0 - style.label_baseline)) + baseline;
|
||||||
let color = Color::BLACK;
|
let color = style.label_color;
|
||||||
|
|
||||||
Label::new(text, alignment, scale, color, left, right, baseline)
|
Label::new(text, alignment, scale, color, left, right, baseline)
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,8 +23,8 @@ impl DialogResponse {
|
||||||
|
|
||||||
pub fn get_color(&self) -> Color {
|
pub fn get_color(&self) -> Color {
|
||||||
match self {
|
match self {
|
||||||
DialogResponse::Yes => Color::BLUE,
|
DialogResponse::Yes => THEME.palette.blue,
|
||||||
DialogResponse::No => Color::RED,
|
DialogResponse::No => THEME.palette.red,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ pub struct DialogStyle {
|
||||||
impl Default for DialogStyle {
|
impl Default for DialogStyle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
rounding: 5.0,
|
rounding: THEME.metrics.surface_rounding,
|
||||||
header: Default::default(),
|
header: Default::default(),
|
||||||
body: Default::default(),
|
body: Default::default(),
|
||||||
footer: Default::default(),
|
footer: Default::default(),
|
||||||
|
@ -61,10 +61,10 @@ pub struct DialogHeaderStyle {
|
||||||
impl Default for DialogHeaderStyle {
|
impl Default for DialogHeaderStyle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
color: Color::WHITE,
|
color: THEME.palette.surface,
|
||||||
height: 20.0,
|
height: 20.0,
|
||||||
text_font: Font::new(crate::DISPLAY_FONT),
|
text_font: Font::new(crate::DISPLAY_FONT),
|
||||||
text_color: Color::BLACK,
|
text_color: THEME.palette.text,
|
||||||
text_scale_factor: 0.65,
|
text_scale_factor: 0.65,
|
||||||
text_baseline: 0.25,
|
text_baseline: 0.25,
|
||||||
}
|
}
|
||||||
|
@ -82,9 +82,9 @@ pub struct DialogBodyStyle {
|
||||||
impl Default for DialogBodyStyle {
|
impl Default for DialogBodyStyle {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
color: Color::WHITE.with_alpha(0xb0),
|
color: THEME.palette.base,
|
||||||
text_font: Font::new(crate::CONTENT_FONT),
|
text_font: Font::new(crate::CONTENT_FONT),
|
||||||
text_color: Color::BLACK,
|
text_color: THEME.palette.text,
|
||||||
text_size: 5.0,
|
text_size: 5.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,7 @@ pub struct DialogFooterStyle {
|
||||||
pub icon_font: Font,
|
pub icon_font: Font,
|
||||||
pub button_radius: f32,
|
pub button_radius: f32,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
|
pub button_fg: Color,
|
||||||
pub height: f32,
|
pub height: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,7 +104,8 @@ impl Default for DialogFooterStyle {
|
||||||
Self {
|
Self {
|
||||||
icon_font: Font::new(crate::ICON_FONT),
|
icon_font: Font::new(crate::ICON_FONT),
|
||||||
button_radius: 7.5,
|
button_radius: 7.5,
|
||||||
color: Color::WHITE,
|
color: THEME.palette.surface,
|
||||||
|
button_fg: THEME.palette.white,
|
||||||
height: 15.0,
|
height: 15.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,7 +138,7 @@ impl Dialog {
|
||||||
thickness: radius * 0.05,
|
thickness: radius * 0.05,
|
||||||
body_color: color,
|
body_color: color,
|
||||||
ring_color: color,
|
ring_color: color,
|
||||||
icon_color: Color::WHITE,
|
icon_color: style.footer.button_fg,
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = LabelText {
|
let text = LabelText {
|
||||||
|
|
|
@ -224,6 +224,7 @@ pub struct TabMenu {
|
||||||
impl TabMenu {
|
impl TabMenu {
|
||||||
const HEAD_RADIUS: f32 = 5.0;
|
const HEAD_RADIUS: f32 = 5.0;
|
||||||
const HEAD_HEIGHT: f32 = 15.0;
|
const HEAD_HEIGHT: f32 = 15.0;
|
||||||
|
const HEAD_COLOR: Color = THEME.palette.surface;
|
||||||
const TAB_WIDTH: f32 = 15.0;
|
const TAB_WIDTH: f32 = 15.0;
|
||||||
const TAB_HEIGHT: f32 = 25.0;
|
const TAB_HEIGHT: f32 = 25.0;
|
||||||
const TAB_NUM: usize = 6;
|
const TAB_NUM: usize = 6;
|
||||||
|
@ -235,9 +236,9 @@ impl TabMenu {
|
||||||
radius: Self::HEAD_HEIGHT * 0.25,
|
radius: Self::HEAD_HEIGHT * 0.25,
|
||||||
spacing: Self::HEAD_HEIGHT * 0.1,
|
spacing: Self::HEAD_HEIGHT * 0.1,
|
||||||
thickness: Self::HEAD_HEIGHT * 0.05,
|
thickness: Self::HEAD_HEIGHT * 0.05,
|
||||||
body_color: Color::WHITE,
|
body_color: Self::HEAD_COLOR,
|
||||||
ring_color: Color::BLACK,
|
ring_color: THEME.palette.black,
|
||||||
icon_color: Color::BLACK,
|
icon_color: THEME.palette.black,
|
||||||
};
|
};
|
||||||
|
|
||||||
const HEAD_BUTTON_MARGIN: f32 = Self::HEAD_HEIGHT / 2.0;
|
const HEAD_BUTTON_MARGIN: f32 = Self::HEAD_HEIGHT / 2.0;
|
||||||
|
@ -348,20 +349,18 @@ impl Container for TabMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&mut self, ctx: &DrawContext) {
|
fn draw(&mut self, ctx: &DrawContext) {
|
||||||
let head_color = Color::WHITE;
|
|
||||||
|
|
||||||
ctx.draw_partially_rounded_rect(
|
ctx.draw_partially_rounded_rect(
|
||||||
CornerFlags::BOTTOM_RIGHT,
|
CornerFlags::BOTTOM_RIGHT,
|
||||||
self.separator_rect,
|
self.separator_rect,
|
||||||
Self::INNER_RADIUS,
|
Self::INNER_RADIUS,
|
||||||
head_color,
|
Self::HEAD_COLOR,
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.draw_partially_rounded_rect(
|
ctx.draw_partially_rounded_rect(
|
||||||
CornerFlags::TOP,
|
CornerFlags::TOP,
|
||||||
self.head_rect,
|
self.head_rect,
|
||||||
Self::HEAD_RADIUS,
|
Self::HEAD_RADIUS,
|
||||||
head_color,
|
Self::HEAD_COLOR,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ impl<T: Container> Widget for T {
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use super::*;
|
pub use super::*;
|
||||||
pub use crate::anim::Animation;
|
pub use crate::anim::Animation;
|
||||||
|
pub use crate::style::{self, THEME};
|
||||||
pub use canary_script::{*, api::*};
|
pub use canary_script::{*, api::*};
|
||||||
pub use keyframe::functions::*;
|
pub use keyframe::functions::*;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,11 +21,11 @@ impl Default for ScrollBarStyle {
|
||||||
margin: Vec2::splat(2.0),
|
margin: Vec2::splat(2.0),
|
||||||
body_radius: 1.0,
|
body_radius: 1.0,
|
||||||
body_width: 3.0,
|
body_width: 3.0,
|
||||||
body_idle_color: Color(0x7f7f7fff),
|
body_idle_color: THEME.palette.base,
|
||||||
body_hover_color: Color(0xb0b0b0ff),
|
body_hover_color: THEME.palette.base_hover,
|
||||||
body_selected_color: Color::WHITE,
|
body_selected_color: THEME.palette.base_active,
|
||||||
rail_width: 1.0,
|
rail_width: 1.0,
|
||||||
rail_color: Color(0xa0a0a07f),
|
rail_color: THEME.palette.base,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue