VIP inspection tooling #4
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -1,10 +1,15 @@
 | 
			
		|||
use std::ops::{Bound, RangeBounds};
 | 
			
		||||
use std::{
 | 
			
		||||
    fmt::Display,
 | 
			
		||||
    ops::{Bound, RangeBounds},
 | 
			
		||||
    str::FromStr,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use egui::{
 | 
			
		||||
    ecolor::HexColor, Align, Color32, CursorIcon, Event, Frame, Key, Layout, Margin, Rect,
 | 
			
		||||
    Response, RichText, Rounding, Sense, Shape, Stroke, TextEdit, Ui, UiBuilder, Vec2, Widget,
 | 
			
		||||
    WidgetText,
 | 
			
		||||
};
 | 
			
		||||
use num_traits::PrimInt;
 | 
			
		||||
 | 
			
		||||
pub trait UiExt {
 | 
			
		||||
    fn section(&mut self, title: impl Into<String>, add_contents: impl FnOnce(&mut Ui));
 | 
			
		||||
| 
						 | 
				
			
			@ -86,37 +91,47 @@ impl UiExt for Ui {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct NumberEdit<'a> {
 | 
			
		||||
    value: &'a mut usize,
 | 
			
		||||
    min: Option<usize>,
 | 
			
		||||
    max: Option<usize>,
 | 
			
		||||
enum Direction {
 | 
			
		||||
    Up,
 | 
			
		||||
    Down,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> NumberEdit<'a> {
 | 
			
		||||
    pub fn new(value: &'a mut usize) -> Self {
 | 
			
		||||
pub trait Number: PrimInt + Display + FromStr + Send + Sync + 'static {}
 | 
			
		||||
impl<T: PrimInt + Display + FromStr + Send + Sync + 'static> Number for T {}
 | 
			
		||||
 | 
			
		||||
pub struct NumberEdit<'a, T: Number> {
 | 
			
		||||
    value: &'a mut T,
 | 
			
		||||
    increment: T,
 | 
			
		||||
    min: Option<T>,
 | 
			
		||||
    max: Option<T>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a, T: Number> NumberEdit<'a, T> {
 | 
			
		||||
    pub fn new(value: &'a mut T) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            value,
 | 
			
		||||
            increment: T::one(),
 | 
			
		||||
            min: None,
 | 
			
		||||
            max: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn range(self, range: impl RangeBounds<usize>) -> Self {
 | 
			
		||||
    pub fn range(self, range: impl RangeBounds<T>) -> Self {
 | 
			
		||||
        let min = match range.start_bound() {
 | 
			
		||||
            Bound::Unbounded => None,
 | 
			
		||||
            Bound::Included(t) => Some(*t),
 | 
			
		||||
            Bound::Excluded(t) => t.checked_add(1),
 | 
			
		||||
            Bound::Excluded(t) => t.checked_add(&self.increment),
 | 
			
		||||
        };
 | 
			
		||||
        let max = match range.end_bound() {
 | 
			
		||||
            Bound::Unbounded => None,
 | 
			
		||||
            Bound::Included(t) => Some(*t),
 | 
			
		||||
            Bound::Excluded(t) => t.checked_sub(1),
 | 
			
		||||
            Bound::Excluded(t) => t.checked_sub(&self.increment),
 | 
			
		||||
        };
 | 
			
		||||
        Self { min, max, ..self }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Widget for NumberEdit<'_> {
 | 
			
		||||
impl<T: Number> Widget for NumberEdit<'_, T> {
 | 
			
		||||
    fn ui(self, ui: &mut Ui) -> Response {
 | 
			
		||||
        let (last_value, mut str, focus) = ui.memory(|m| {
 | 
			
		||||
            let (lv, s) = m
 | 
			
		||||
| 
						 | 
				
			
			@ -131,7 +146,7 @@ impl Widget for NumberEdit<'_> {
 | 
			
		|||
            str = self.value.to_string();
 | 
			
		||||
            stale = true;
 | 
			
		||||
        }
 | 
			
		||||
        let valid = str.parse().is_ok_and(|v: usize| v == *self.value);
 | 
			
		||||
        let valid = str.parse().is_ok_and(|v: T| v == *self.value);
 | 
			
		||||
        let mut up_pressed = false;
 | 
			
		||||
        let mut down_pressed = false;
 | 
			
		||||
        if focus {
 | 
			
		||||
| 
						 | 
				
			
			@ -192,26 +207,30 @@ impl Widget for NumberEdit<'_> {
 | 
			
		|||
        let arrow_middle = (res.rect.min.y + res.rect.max.y) / 2.0;
 | 
			
		||||
        let arrow_bottom = res.rect.max.y + 2.0;
 | 
			
		||||
 | 
			
		||||
        let mut delta = 0;
 | 
			
		||||
        let mut delta = None;
 | 
			
		||||
        let top_arrow_rect = Rect {
 | 
			
		||||
            min: (arrow_left, arrow_top).into(),
 | 
			
		||||
            max: (arrow_right, arrow_middle).into(),
 | 
			
		||||
        };
 | 
			
		||||
        if draw_arrow(ui, top_arrow_rect, true).clicked_or_dragged() || up_pressed {
 | 
			
		||||
            delta = 1;
 | 
			
		||||
            delta = Some(Direction::Up);
 | 
			
		||||
        }
 | 
			
		||||
        let bottom_arrow_rect = Rect {
 | 
			
		||||
            min: (arrow_left, arrow_middle).into(),
 | 
			
		||||
            max: (arrow_right, arrow_bottom).into(),
 | 
			
		||||
        };
 | 
			
		||||
        if draw_arrow(ui, bottom_arrow_rect, false).clicked_or_dragged() || down_pressed {
 | 
			
		||||
            delta = -1;
 | 
			
		||||
            delta = Some(Direction::Down);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let in_range =
 | 
			
		||||
            |&val: &usize| self.min.is_none_or(|m| m <= val) && self.max.is_none_or(|m| m >= val);
 | 
			
		||||
        if delta != 0 {
 | 
			
		||||
            if let Some(new_value) = self.value.checked_add_signed(delta).filter(in_range) {
 | 
			
		||||
            |val: &T| self.min.is_none_or(|m| &m <= val) && self.max.is_none_or(|m| &m >= val);
 | 
			
		||||
        if let Some(dir) = delta {
 | 
			
		||||
            let value = match dir {
 | 
			
		||||
                Direction::Up => self.value.checked_add(&self.increment),
 | 
			
		||||
                Direction::Down => self.value.checked_sub(&self.increment),
 | 
			
		||||
            };
 | 
			
		||||
            if let Some(new_value) = value.filter(in_range) {
 | 
			
		||||
                *self.value = new_value;
 | 
			
		||||
            }
 | 
			
		||||
            str = self.value.to_string();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -62,6 +62,9 @@ impl ObjectWindow {
 | 
			
		|||
                });
 | 
			
		||||
            ui.section("Properties", |ui| {
 | 
			
		||||
                let object = self.objects.borrow().read::<[u16; 4]>(self.index);
 | 
			
		||||
                let mut x = ((object[0] & 0x3ff) << 6 >> 6) as i16;
 | 
			
		||||
                let mut parallax = ((object[1] & 0x3ff) << 6 >> 6) as i16;
 | 
			
		||||
                let mut y = ((object[2] & 0x0ff) << 8 >> 8) as i16;
 | 
			
		||||
                let (mut char_index, mut vflip, mut hflip, palette_index) =
 | 
			
		||||
                    utils::parse_cell(object[3]);
 | 
			
		||||
                TableBuilder::new(ui)
 | 
			
		||||
| 
						 | 
				
			
			@ -91,6 +94,33 @@ impl ObjectWindow {
 | 
			
		|||
                                );
 | 
			
		||||
                            });
 | 
			
		||||
                        });
 | 
			
		||||
                        body.row(row_height, |mut row| {
 | 
			
		||||
                            row.col(|ui| {
 | 
			
		||||
                                ui.label("X");
 | 
			
		||||
                            });
 | 
			
		||||
                            row.col(|ui| {
 | 
			
		||||
                                ui.add_enabled(false, NumberEdit::new(&mut x).range(-512..512));
 | 
			
		||||
                            });
 | 
			
		||||
                        });
 | 
			
		||||
                        body.row(row_height, |mut row| {
 | 
			
		||||
                            row.col(|ui| {
 | 
			
		||||
                                ui.label("Y");
 | 
			
		||||
                            });
 | 
			
		||||
                            row.col(|ui| {
 | 
			
		||||
                                ui.add_enabled(false, NumberEdit::new(&mut y).range(-8..=224));
 | 
			
		||||
                            });
 | 
			
		||||
                        });
 | 
			
		||||
                        body.row(row_height, |mut row| {
 | 
			
		||||
                            row.col(|ui| {
 | 
			
		||||
                                ui.label("Parallax");
 | 
			
		||||
                            });
 | 
			
		||||
                            row.col(|ui| {
 | 
			
		||||
                                ui.add_enabled(
 | 
			
		||||
                                    false,
 | 
			
		||||
                                    NumberEdit::new(&mut parallax).range(-512..512),
 | 
			
		||||
                                );
 | 
			
		||||
                            });
 | 
			
		||||
                        });
 | 
			
		||||
                        body.row(row_height, |mut row| {
 | 
			
		||||
                            row.col(|ui| {
 | 
			
		||||
                                let checkbox = Checkbox::new(&mut hflip, "H-flip");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue