My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/games/rstnode/rst-client/src/ui/button.rs

119 lines
3.4 KiB

use crate::{color::ColorUtils, get_tx, EventId, HandlerDiff};
use ggez::graphics::{self, Align, Color, DrawMode, DrawParam, Drawable, MeshBuilder, Rect, Text};
use ggez::{
event::{EventHandler, MouseButton},
Context, GameResult,
};
use rst_core::data::Pos;
use std::collections::HashMap;
/// A button with next that can be clicked to run some code
pub struct Button {
pos: Pos,
size: Pos,
text: Option<String>,
color_base: Color,
color_trim: Color,
cbs: HashMap<MouseButton, Box<dyn FnMut() -> ()>>,
}
impl Button {
/// Create a new button
pub fn new(
pos: Pos,
size: Pos,
text: impl Into<Option<String>>,
color: impl Into<Color>,
) -> Self {
let color = color.into();
Self {
pos,
size,
text: text.into(),
color_base: color.clone(),
color_trim: color.brighten(2),
cbs: HashMap::new(),
}
}
/// Register an on-click listening closure
pub fn on_click<C: FnMut() -> () + 'static>(mut self, btn: MouseButton, cb: C) -> Self {
self.cbs.insert(btn, Box::new(cb));
self
}
/// Register the button with the event handler
pub fn register(self) -> EventId {
let (btn_id, diff) = HandlerDiff::add(self);
get_tx().send(diff).unwrap();
btn_id
}
fn boundry_check(&self, x: f32, y: f32) -> bool {
self.pos.x < x
&& (self.pos.x + self.size.x) > x
&& self.pos.y < y
&& (self.pos.y + self.size.y) > y
}
}
impl EventHandler for Button {
fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
Ok(())
}
fn mouse_button_down_event(&mut self, _ctx: &mut Context, btn: MouseButton, x: f32, y: f32) {
if self.boundry_check(x, y) {
trace!("Triggering button state");
std::mem::swap(&mut self.color_base, &mut self.color_trim);
if let Some(ref mut callback) = self.cbs.get_mut(&btn) {
callback();
}
}
}
fn mouse_button_up_event(&mut self, _ctx: &mut Context, btn: MouseButton, x: f32, y: f32) {
if self.boundry_check(x, y) {
trace!("Releasing button state");
std::mem::swap(&mut self.color_base, &mut self.color_trim);
}
}
fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
let mut mb = MeshBuilder::new();
let bounds = Rect {
x: self.pos.x,
y: self.pos.y,
w: self.size.x,
h: self.size.y,
};
// Create the basic building blocks
let rect = mb
.rectangle(DrawMode::fill(), bounds, self.color_base.clone())?
.build(ctx)?;
let frame = mb
.rectangle(DrawMode::stroke(2.0), bounds, self.color_trim.clone())?
.build(ctx)?;
rect.draw(ctx, DrawParam::new())?;
frame.draw(ctx, DrawParam::new())?;
if let Some(ref text) = self.text {
let Pos { x, y } = self.pos;
let Pos { x: w, y: h } = self.size;
let mut text = Text::new(text.clone());
text.draw(
ctx,
DrawParam::new().dest([
x + (w / 2.0 - text.width(ctx) / 2.0),
y + (h / 2.0 - text.height(ctx) / 2.0),
]),
)?;
}
Ok(())
}
}