SAO UI button icons + Label widget
This commit is contained in:
parent
b7df68195a
commit
2e36ef9851
|
@ -12,7 +12,3 @@ glam = "^0.21"
|
|||
keyframe = "1"
|
||||
canary_script = { path = "../script", features = ["glam"] }
|
||||
wee_alloc = "^0.4"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
lto = "fat"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use canary_script::Panel;
|
||||
use crate::{Color, Vec2};
|
||||
use bitflags::bitflags;
|
||||
use canary_script::GlyphPosition;
|
||||
use canary_script::Panel;
|
||||
|
||||
pub enum Corner {
|
||||
TopRight,
|
||||
|
@ -81,6 +82,13 @@ impl Rect {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn scale(&self, scale: f32) -> Self {
|
||||
Self {
|
||||
bl: self.bl * scale,
|
||||
tr: self.tr * scale,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.bl.cmplt(self.tr).all()
|
||||
}
|
||||
|
@ -122,6 +130,15 @@ impl Rect {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<canary_script::Rect> for Rect {
|
||||
fn from(other: canary_script::Rect) -> Self {
|
||||
Self {
|
||||
bl: other.bl.into(),
|
||||
tr: other.tr.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DrawContext {
|
||||
panel: Panel,
|
||||
offset: Option<Vec2>,
|
||||
|
@ -163,7 +180,8 @@ impl DrawContext {
|
|||
color.a *= opacity;
|
||||
}
|
||||
|
||||
self.panel.draw_triangle(v1.into(), v2.into(), v3.into(), color);
|
||||
self.panel
|
||||
.draw_triangle(v1.into(), v2.into(), v3.into(), color);
|
||||
}
|
||||
|
||||
pub fn draw_circle(&self, center: Vec2, radius: f32, color: Color) {
|
||||
|
@ -312,6 +330,24 @@ impl DrawContext {
|
|||
self.draw_rect(inner_rect, color);
|
||||
}
|
||||
|
||||
pub fn draw_text(
|
||||
&self,
|
||||
glyphs: &[GlyphPosition],
|
||||
mut offset: Vec2,
|
||||
scale: f32,
|
||||
mut color: Color,
|
||||
) {
|
||||
if let Some(delta) = self.offset {
|
||||
offset += delta;
|
||||
}
|
||||
|
||||
if let Some(opacity) = self.opacity {
|
||||
color.a *= opacity;
|
||||
}
|
||||
|
||||
self.panel.draw_glyphs(glyphs, offset.into(), scale, color);
|
||||
}
|
||||
|
||||
pub fn get_clip_rect(&self) -> &Option<Rect> {
|
||||
&self.clip_rect
|
||||
}
|
||||
|
|
|
@ -34,24 +34,6 @@ impl PanelImpl for DummyPanel {
|
|||
fn draw(&mut self) {
|
||||
let ctx = draw::DrawContext::new(self.panel);
|
||||
self.menu.draw(&ctx);
|
||||
|
||||
let font = Font::new("Iosevka Nerd Font");
|
||||
let text = "";
|
||||
let bg = Color::MAGENTA;
|
||||
let fg = Color::BLACK;
|
||||
let scale = 0.1;
|
||||
let pos = Vec2::ZERO;
|
||||
|
||||
let layout = TextLayout::new(&font, text);
|
||||
let bounds = layout.get_bounds();
|
||||
let bounds = draw::Rect {
|
||||
bl: Vec2::from(bounds.bl) * scale,
|
||||
tr: Vec2::from(bounds.tr) * scale,
|
||||
};
|
||||
|
||||
ctx.draw_rect(bounds.offset(pos), bg);
|
||||
let glyphs = layout.get_glyphs();
|
||||
self.panel.draw_glyphs(&glyphs, pos.into(), scale, fg);
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: canary_script::Vec2) {
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use crate::anim::Animation;
|
||||
use crate::draw::{CornerFlags, DrawContext, Rect};
|
||||
use crate::{Color, CursorEventKind, Vec2};
|
||||
use canary_script::{Font, GlyphPosition, TextLayout};
|
||||
use keyframe::functions::*;
|
||||
|
||||
pub trait Widget {
|
||||
fn update(&mut self, dt: f32);
|
||||
fn draw(&mut self, ctx: &DrawContext);
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2);
|
||||
fn update(&mut self, dt: f32) {}
|
||||
fn draw(&mut self, ctx: &DrawContext) {}
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {}
|
||||
}
|
||||
|
||||
pub trait Button {
|
||||
|
@ -25,26 +26,132 @@ pub enum ButtonState {
|
|||
Releasing,
|
||||
}
|
||||
|
||||
pub struct RoundButton {
|
||||
pub pos: Vec2,
|
||||
pub radius: f32,
|
||||
pub spacing: f32,
|
||||
pub thickness: f32,
|
||||
pub shrink_anim: Animation<EaseOutQuint>,
|
||||
pub was_clicked: bool,
|
||||
pub state: ButtonState,
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum HorizontalAlignment {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Default for RoundButton {
|
||||
fn default() -> Self {
|
||||
pub struct LabelText {
|
||||
pub font: Font,
|
||||
pub text: String,
|
||||
}
|
||||
|
||||
pub struct Label {
|
||||
text: LabelText,
|
||||
alignment: HorizontalAlignment,
|
||||
scale: f32,
|
||||
color: Color,
|
||||
left: f32,
|
||||
right: f32,
|
||||
baseline: f32,
|
||||
center_y: bool,
|
||||
dirty: bool,
|
||||
glyphs: Vec<GlyphPosition>,
|
||||
bounds: Rect,
|
||||
offset: Vec2,
|
||||
}
|
||||
|
||||
impl Label {
|
||||
pub fn new(
|
||||
text: LabelText,
|
||||
alignment: HorizontalAlignment,
|
||||
scale: f32,
|
||||
color: Color,
|
||||
left: f32,
|
||||
right: f32,
|
||||
baseline: f32,
|
||||
center_y: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
text,
|
||||
alignment,
|
||||
scale,
|
||||
color,
|
||||
left,
|
||||
right,
|
||||
baseline,
|
||||
center_y,
|
||||
dirty: true,
|
||||
glyphs: Vec::new(),
|
||||
bounds: Rect::from_xy_size(Vec2::ZERO, Vec2::ZERO),
|
||||
offset: Vec2::ZERO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for Label {
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
if self.dirty {
|
||||
let layout = TextLayout::new(&self.text.font, &self.text.text);
|
||||
let bounds = Rect::from(layout.get_bounds()).scale(self.scale);
|
||||
self.bounds = bounds;
|
||||
self.glyphs = layout.get_glyphs();
|
||||
let xoff = match self.alignment {
|
||||
HorizontalAlignment::Left => self.left - bounds.bl.x,
|
||||
HorizontalAlignment::Right => self.right - bounds.tr.x,
|
||||
HorizontalAlignment::Center => {
|
||||
let available = self.right - self.left;
|
||||
let halfway = available / 2.0 + self.left;
|
||||
let width = bounds.tr.x - bounds.bl.x;
|
||||
let left = halfway - width / 2.0;
|
||||
left - bounds.bl.x
|
||||
}
|
||||
};
|
||||
|
||||
let yoff = if self.center_y {
|
||||
(bounds.bl.y - bounds.tr.y) / 2.0 - bounds.bl.y
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
self.offset = Vec2::new(xoff, yoff + self.baseline);
|
||||
self.dirty = false;
|
||||
}
|
||||
|
||||
ctx.draw_text(&self.glyphs, self.offset, self.scale, self.color);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RoundButton {
|
||||
pos: Vec2,
|
||||
radius: f32,
|
||||
spacing: f32,
|
||||
thickness: f32,
|
||||
shrink_anim: Animation<EaseOutQuint>,
|
||||
was_clicked: bool,
|
||||
state: ButtonState,
|
||||
label: Option<Label>,
|
||||
}
|
||||
|
||||
impl RoundButton {
|
||||
pub fn new(label: Option<LabelText>) -> Self {
|
||||
let radius = 0.05;
|
||||
|
||||
let label = label.map(|text| {
|
||||
let scale = radius * 1.5;
|
||||
let alignment = HorizontalAlignment::Center;
|
||||
let left = -radius;
|
||||
let right = radius;
|
||||
let baseline = 0.0;
|
||||
let color = Color::BLACK;
|
||||
let center_y = true;
|
||||
|
||||
Label::new(
|
||||
text, alignment, scale, color, left, right, baseline, center_y,
|
||||
)
|
||||
});
|
||||
|
||||
Self {
|
||||
pos: Default::default(),
|
||||
radius: 0.02,
|
||||
spacing: 0.005,
|
||||
thickness: 0.001,
|
||||
radius,
|
||||
spacing: 0.01,
|
||||
thickness: 0.002,
|
||||
shrink_anim: Animation::new(EaseOutQuint, 0.1, 1.0, 0.0),
|
||||
was_clicked: false,
|
||||
state: ButtonState::Idle,
|
||||
label,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,6 +179,10 @@ impl Widget for RoundButton {
|
|||
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);
|
||||
|
||||
if let Some(label) = self.label.as_mut() {
|
||||
label.draw(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {
|
||||
|
@ -714,14 +825,22 @@ pub struct MainMenu {
|
|||
}
|
||||
|
||||
impl MainMenu {
|
||||
pub const SUBMENU_SPACING: f32 = 0.05;
|
||||
pub const SUBMENU_SPACING: f32 = 0.1;
|
||||
}
|
||||
|
||||
impl Default for MainMenu {
|
||||
fn default() -> Self {
|
||||
let icon_font = Font::new("Iosevka Nerd Font");
|
||||
let icons = ["", "", "", "", ""];
|
||||
|
||||
let mut buttons = Vec::new();
|
||||
for _ in 0..5 {
|
||||
let button = RoundButton::default();
|
||||
for icon in icons {
|
||||
let text = LabelText {
|
||||
font: icon_font,
|
||||
text: icon.to_string(),
|
||||
};
|
||||
|
||||
let button = RoundButton::new(Some(text));
|
||||
buttons.push(button);
|
||||
}
|
||||
|
||||
|
@ -730,7 +849,7 @@ impl Default for MainMenu {
|
|||
let inventory = Reveal::new(inventory, -0.02, 0.1);
|
||||
|
||||
Self {
|
||||
menu: ScrollMenu::new(buttons, 0.1),
|
||||
menu: ScrollMenu::new(buttons, 0.2),
|
||||
inventory,
|
||||
}
|
||||
}
|
||||
|
@ -773,14 +892,14 @@ pub struct TabMenu {
|
|||
}
|
||||
|
||||
impl TabMenu {
|
||||
const HEAD_RADIUS: f32 = 0.025;
|
||||
const HEAD_HEIGHT: f32 = 0.03;
|
||||
const TAB_WIDTH: f32 = 0.04;
|
||||
const TAB_HEIGHT: f32 = 0.06;
|
||||
const HEAD_RADIUS: f32 = 0.05;
|
||||
const HEAD_HEIGHT: f32 = 0.1;
|
||||
const TAB_WIDTH: f32 = 0.1;
|
||||
const TAB_HEIGHT: f32 = 0.15;
|
||||
const TAB_NUM: usize = 6;
|
||||
const SEPARATOR_WIDTH: f32 = 0.015;
|
||||
const INNER_RADIUS: f32 = 0.005;
|
||||
const CONTENT_WIDTH: f32 = 0.4;
|
||||
const SEPARATOR_WIDTH: f32 = 0.02;
|
||||
const INNER_RADIUS: f32 = 0.01;
|
||||
const CONTENT_WIDTH: f32 = 0.64;
|
||||
|
||||
pub fn new() -> Self {
|
||||
let tab_size = Vec2::new(Self::TAB_WIDTH, Self::TAB_HEIGHT);
|
||||
|
@ -901,7 +1020,7 @@ pub struct Inventory {
|
|||
|
||||
impl Inventory {
|
||||
pub fn new(available_width: f32) -> (Self, f32) {
|
||||
let height = 1.5;
|
||||
let height = 1.28;
|
||||
|
||||
(
|
||||
Self {
|
||||
|
@ -914,25 +1033,22 @@ impl Inventory {
|
|||
}
|
||||
|
||||
impl Widget for Inventory {
|
||||
fn update(&mut self, dt: f32) {}
|
||||
|
||||
fn draw(&mut self, ctx: &DrawContext) {
|
||||
let box_size = 0.04;
|
||||
let box_margin = 0.01;
|
||||
let box_size = 0.06;
|
||||
let box_margin = 0.02;
|
||||
let box_stride = box_size + box_margin;
|
||||
|
||||
let grid_width = (self.width / box_stride).floor() as usize;
|
||||
let grid_height = (self.height / box_stride).floor() as usize;
|
||||
let grid_offset = Vec2::new(box_margin, box_margin) / 2.0;
|
||||
|
||||
for x in 0..grid_width {
|
||||
for y in 0..grid_height {
|
||||
let off = Vec2::new(x as f32, y as f32) * box_stride;
|
||||
let off = Vec2::new(x as f32, y as f32) * box_stride + grid_offset;
|
||||
let rect = Rect::from_xy_size(off, Vec2::new(box_size, box_size));
|
||||
let color = Color::MAGENTA;
|
||||
ctx.draw_rect(rect, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue