add tooltips

This commit is contained in:
Crispy 2024-12-19 20:24:08 +01:00
parent fea00da8a3
commit 4d05d5a3ee
4 changed files with 235 additions and 70 deletions

View file

@ -14,7 +14,6 @@ logic mostly like https://git.crispypin.cc/CrispyPin/marble
- make direct power (comparator -> machine) work, (needs storing power direction in machine tiles)
- cut selections, copy to system clipboard
- timestamps in solutions and blueprints
- tooltips
- lock tile types for early levels to make it less overwhelming
- display tool variant more clearly (it's not obvious there are more states)
- option to use 8-bit marbles?

View file

@ -21,7 +21,7 @@ use crate::{
solution::{Score, Solution},
text_input, texture_option_button,
theme::*,
userdata_dir, Scroll, Textures, TILE_TEXTURE_SIZE,
userdata_dir, Scroll, Textures, Tooltip, TILE_TEXTURE_SIZE,
};
const HEADER_HEIGHT: i32 = 40;
@ -64,6 +64,7 @@ pub struct Editor {
total_steps: usize,
popup: EndPopup,
score: Option<Score>,
tooltip: Tooltip,
blueprints: Vec<Blueprint>,
selected_blueprint: usize,
@ -176,6 +177,7 @@ impl Editor {
draw_overlay: true,
undo_history: Vec::new(),
undo_index: 0,
tooltip: Tooltip::default(),
}
}
@ -567,6 +569,8 @@ impl Editor {
}
}
self.tooltip.init_frame(d);
self.draw_board(d, textures);
self.board_overlay(d, textures);
self.draw_bottom_bar(d, textures);
@ -593,6 +597,7 @@ impl Editor {
.enumerate()
{
let i = i + self.blueprint_scroll;
self.tooltip.add(5, y, 32, 32, "Delete");
if simple_button(d, 5, y, 32, 32) {
b.remove_file();
self.blueprints.remove(i);
@ -612,6 +617,7 @@ impl Editor {
if is_selected {
self.blueprint_name_selected = text_selected;
}
self.tooltip.add(42 + 205, y, 32, 32, "Select");
simple_option_button(d, 42 + 205, y, 32, 32, i, &mut self.selected_blueprint);
d.draw_texture_ex(
@ -625,6 +631,8 @@ impl Editor {
}
}
self.tooltip.draw(d);
if matches!(self.popup, EndPopup::Success | EndPopup::Failure) {
let x = d.get_screen_width() / 2 - POPUP_WIDTH / 2;
let y = d.get_screen_height() / 2 - POPUP_HEIGHT / 2;
@ -694,6 +702,7 @@ impl Editor {
}
if self.sim_state == SimState::Editing {
self.tooltip.add(150, 4, 32, 32, "Undo");
if simple_button(d, 150, 4, 32, 32) {
self.undo()
}
@ -704,6 +713,7 @@ impl Editor {
};
draw_scaled_texture(d, textures.get(undo_icon), 150, 4, 2.);
self.tooltip.add(186, 4, 32, 32, "Redo");
if simple_button(d, 186, 4, 32, 32) {
self.redo()
}
@ -715,48 +725,48 @@ impl Editor {
draw_scaled_texture(d, textures.get(redo_icon), 186, 4, 2.);
}
simple_toggle_button(d, &mut self.draw_overlay, 223, 4, 32, 32, 4);
self.tooltip.add(223, 4, 32, 32, "Toggle overlay");
match self.sim_state {
SimState::Editing => {
if simple_button(d, 260, 4, 32, 32) {
self.init_sim();
self.sim_state = SimState::Running;
}
draw_scaled_texture(d, textures.get("play"), 260, 4, 2.);
if self.sim_state == SimState::Running {
self.tooltip.add(260, 4, 32, 32, "Pause");
if simple_button(d, 260, 4, 32, 32) {
self.sim_state = SimState::Stepping;
}
SimState::Running => {
if simple_button(d, 260, 4, 32, 32) {
self.sim_state = SimState::Stepping;
draw_scaled_texture(d, textures.get("pause"), 260, 4, 2.);
} else {
self.tooltip.add(260, 4, 32, 32, "Start");
if simple_button(d, 260, 4, 32, 32) {
if self.sim_state == SimState::Editing {
self.init_sim()
}
draw_scaled_texture(d, textures.get("pause"), 260, 4, 2.);
if simple_button(d, 296, 4, 32, 32) {
self.sim_state = SimState::Editing;
self.popup = EndPopup::None;
}
draw_scaled_texture(d, textures.get("stop"), 296, 4, 2.);
}
SimState::Stepping => {
if simple_button(d, 260, 4, 32, 32) {
self.sim_state = SimState::Running;
}
draw_scaled_texture(d, textures.get("play"), 260, 4, 2.);
if simple_button(d, 296, 4, 32, 32) {
self.sim_state = SimState::Editing;
self.popup = EndPopup::None;
}
draw_scaled_texture(d, textures.get("stop"), 296, 4, 2.);
self.sim_state = SimState::Running;
}
draw_scaled_texture(d, textures.get("play"), 260, 4, 2.);
}
if self.sim_state != SimState::Editing {
self.tooltip.add(296, 4, 32, 32, "Stop");
if simple_button(d, 296, 4, 32, 32) {
self.sim_state = SimState::Editing;
self.popup = EndPopup::None;
}
draw_scaled_texture(d, textures.get("stop"), 296, 4, 2.);
}
self.tooltip.add(332, 4, 32, 32, "Step");
if simple_button(d, 332, 4, 32, 32) {
self.step_pressed();
}
draw_scaled_texture(d, textures.get("step"), 332, 4, 2.);
self.tooltip.add(368, 4, 48, 32, "Speed");
draw_usize(d, textures, 1 << self.sim_speed, 368, 4, SPEED_DIGITS, 1);
slider(d, &mut self.sim_speed, 0, MAX_SPEED_POWER, 368, 24, 48, 12);
self.tooltip.add(420, 4, 180, 32, "Steps");
draw_usize(d, textures, self.machine.step_count(), 420, 4, 9, 2);
if self.stage > Some(0) {
self.tooltip.add(420, 44, 180, 32, "Total steps");
let total_steps = self.total_steps + self.machine.step_count();
draw_usize(d, textures, total_steps, 420, 44, 9, 2);
}
@ -875,50 +885,88 @@ impl Editor {
}
let mouse_pos = d.get_mouse_position();
let mut tool_button = |(row, col): (i32, i32), texture: &str, tool_option: Tool| {
let border = 4.;
let gap = 2.;
let tex_size = 32.;
let button_size = tex_size + border * 2.;
let grid_size = button_size + gap * 2.;
let pos = Vector2 {
x: 100. + col as f32 * grid_size - if col < 0 { 10. } else { 0. },
y: footer_top + 5. + row as f32 * grid_size,
let mut tool_button =
|(row, col): (i32, i32), texture: &str, tooltip: &'static str, tool_option: Tool| {
let border = 4.;
let gap = 2.;
let tex_size = 32.;
let button_size = tex_size + border * 2.;
let grid_size = button_size + gap * 2.;
let pos = Vector2 {
x: 100. + col as f32 * grid_size - if col < 0 { 10. } else { 0. },
y: footer_top + 5. + row as f32 * grid_size,
};
texture_option_button(
d,
pos,
textures.get(texture),
tool_option,
&mut self.active_tool,
tex_size,
border,
);
let bounds = Rectangle::new(pos.x, pos.y, button_size, button_size);
self.tooltip.add_rec(bounds, tooltip);
if bounds.check_collision_point_rec(mouse_pos) {
get_scroll(d)
} else {
None
}
};
texture_option_button(
d,
pos,
textures.get(texture),
tool_option,
&mut self.active_tool,
tex_size,
border,
);
let bounds = Rectangle::new(pos.x, pos.y, button_size, button_size);
if bounds.check_collision_point_rec(mouse_pos) {
get_scroll(d)
} else {
None
}
};
tool_button((0, -2), "eraser", Tool::Erase);
tool_button((1, -2), "selection", Tool::SelectArea(Selection::default()));
tool_button((0, -2), "eraser", "Eraser", Tool::Erase);
tool_button(
(1, -2),
"selection",
"Select",
Tool::SelectArea(Selection::default()),
);
tool_button((0, -1), "blueprint", Tool::Blueprint);
tool_button((1, -1), "transparent", Tool::None);
tool_button((0, -1), "blueprint", "Blueprints", Tool::Blueprint);
tool_button((1, -1), "transparent", "None", Tool::None);
if !hide_tile_tools {
tool_button((0, 0), "block", Tool::SetTile(Tile::from_char('#')));
tool_button((0, 1), "silo_off", Tool::SetTile(Tile::from_char('B')));
tool_button((0, 2), "button_off", Tool::SetTile(Tile::from_char('*')));
tool_button((0, 3), "io_tile_off", Tool::SetTile(Tile::from_char('I')));
tool_button((0, 4), "flipper_off", Tool::SetTile(Tile::from_char('F')));
tool_button((0, 5), "digit_tool", Tool::Digits(None));
tool_button(
(0, 0),
"block",
"Block",
Tool::SetTile(Tile::from_char('#')),
);
tool_button(
(0, 1),
"silo_off",
"Silo",
Tool::SetTile(Tile::from_char('B')),
);
tool_button(
(0, 2),
"button_off",
"Button",
Tool::SetTile(Tile::from_char('*')),
);
tool_button(
(0, 3),
"io_tile_off",
"Input/Output silo",
Tool::SetTile(Tile::from_char('I')),
);
tool_button(
(0, 4),
"flipper_off",
"Flipper",
Tool::SetTile(Tile::from_char('F')),
);
tool_button((0, 5), "digit_tool", "Digit", Tool::Digits(None));
tool_button((1, 0), "marble", Tool::SetTile(Tile::from_char('o')));
tool_button(
(1, 0),
"marble",
"Marble",
Tool::SetTile(Tile::from_char('o')),
);
match tool_button(
(1, 1),
Tile::Wire(self.tool_wire, false).texture(),
self.tool_wire.texture_name_off(),
self.tool_wire.human_name(),
Tool::Wire,
) {
Some(Scroll::Down) => self.tool_wire.next(),
@ -926,14 +974,20 @@ impl Editor {
None => (),
}
match tool_button((1, 2), Tile::Arrow(self.tool_arrow).texture(), Tool::Arrow) {
match tool_button(
(1, 2),
self.tool_arrow.arrow_tile_texture_name(),
self.tool_arrow.arrow_tile_human_name(),
Tool::Arrow,
) {
Some(Scroll::Down) => self.tool_arrow = self.tool_arrow.right(),
Some(Scroll::Up) => self.tool_arrow = self.tool_arrow.left(),
None => (),
}
if tool_button(
(1, 3),
Tile::Mirror(self.tool_mirror).texture(),
self.tool_mirror.texture_name(),
self.tool_mirror.human_name(),
Tool::Mirror,
)
.is_some()
@ -942,7 +996,8 @@ impl Editor {
}
match tool_button(
(1, 4),
Tile::Powerable(PTile::Math(self.tool_math), false).texture(),
self.tool_math.texture_name_off(),
self.tool_math.human_name(),
Tool::Math,
) {
Some(Scroll::Down) => self.tool_math.next(),
@ -951,7 +1006,8 @@ impl Editor {
}
match tool_button(
(1, 5),
Tile::Powerable(PTile::Comparator(self.tool_comparator), false).texture(),
self.tool_comparator.texture_name_off(),
self.tool_comparator.human_name(),
Tool::Comparator,
) {
Some(Scroll::Down) => self.tool_comparator.next(),

View file

@ -270,6 +270,15 @@ impl Direction {
}
}
pub const fn arrow_tile_human_name(self) -> &'static str {
match self {
Direction::Up => "Up Arrow",
Direction::Down => "Down Arrow",
Direction::Left => "Left Arrow",
Direction::Right => "Right Arrow",
}
}
pub const fn arrow_texture_name(self) -> &'static str {
match self {
Direction::Up => "direction_up",
@ -320,6 +329,7 @@ impl WireType {
WireType::Cross => "wire_cross_on",
}
}
pub const fn texture_name_off(self) -> &'static str {
match self {
WireType::Vertical => "wire_vertical_off",
@ -327,6 +337,14 @@ impl WireType {
WireType::Cross => "wire_cross_off",
}
}
pub const fn human_name(self) -> &'static str {
match self {
WireType::Vertical => "Vertical Wire",
WireType::Horizontal => "Horizontal Wire",
WireType::Cross => "Wire Cross",
}
}
}
impl MirrorType {
@ -360,9 +378,26 @@ impl MirrorType {
MirrorType::Back => "mirror_back",
}
}
pub const fn human_name(self) -> &'static str {
match self {
MirrorType::Forward => "Mirror A",
MirrorType::Back => "Mirror B",
}
}
}
impl MathOp {
pub const fn human_name(self) -> &'static str {
match self {
MathOp::Add => "Math: Add",
MathOp::Sub => "Math: Subtract",
MathOp::Mul => "Math: Multiply",
MathOp::Div => "Math: Divide",
MathOp::Rem => "Math: Remainder",
}
}
pub const fn texture_name_on(self) -> &'static str {
match self {
MathOp::Add => "add_on",
@ -372,6 +407,7 @@ impl MathOp {
MathOp::Rem => "rem_on",
}
}
pub const fn texture_name_off(self) -> &'static str {
match self {
MathOp::Add => "add_off",
@ -404,6 +440,15 @@ impl MathOp {
}
impl Comparison {
pub const fn human_name(self) -> &'static str {
match self {
Comparison::LessThan => "Comparator: Less than",
Comparison::GreaterThan => "Comparator: Greater than",
Comparison::Equal => "Comparator: Equal",
Comparison::NotEqual => "Comparator: Not Equal",
}
}
pub const fn texture_name_on(self) -> &'static str {
match self {
Comparison::LessThan => "lt_on",
@ -412,6 +457,7 @@ impl Comparison {
Comparison::NotEqual => "neq_on",
}
}
pub const fn texture_name_off(self) -> &'static str {
match self {
Comparison::LessThan => "lt_off",

View file

@ -44,6 +44,70 @@ pub fn simple_button(d: &mut RaylibDrawHandle, x: i32, y: i32, width: i32, heigh
pressed
}
#[derive(Debug, Default)]
pub struct Tooltip {
text: Option<&'static str>,
mouse_x: i32,
mouse_y: i32,
screen_width: i32,
screen_height: i32,
}
impl Tooltip {
pub fn init_frame(&mut self, d: &mut RaylibDrawHandle) {
let p = d.get_mouse_position();
*self = Self {
text: None,
mouse_x: p.x as i32,
mouse_y: p.y as i32,
screen_width: d.get_screen_width(),
screen_height: d.get_screen_height(),
};
}
pub fn add(&mut self, x: i32, y: i32, width: i32, height: i32, text: &'static str) {
if self.mouse_x >= x
&& self.mouse_y >= y
&& self.mouse_x <= (x + width)
&& self.mouse_y <= (y + height)
{
self.text = Some(text);
}
}
pub fn add_rec(&mut self, bounds: Rectangle, text: &'static str) {
self.add(
bounds.x as i32,
bounds.y as i32,
bounds.width as i32,
bounds.height as i32,
text,
);
}
pub fn draw(&self, d: &mut RaylibDrawHandle) {
if let Some(text) = self.text {
let font_size = 20;
let margin = 4;
let text_width = d.measure_text(text, font_size);
let x = self
.mouse_x
.min(self.screen_width - text_width - margin * 2);
let y = self
.mouse_y
.min(self.screen_height - font_size - margin * 2);
d.draw_rectangle(
x,
y,
text_width + margin * 2,
font_size + margin * 2,
BG_LIGHT,
);
d.draw_text(text, x + margin, y + margin, font_size, Color::WHITE);
}
}
}
pub fn simple_toggle_button(
d: &mut RaylibDrawHandle,
state: &mut bool,