use crate::canvas::PixelRect;
use crate::coord::Coord;
use crate::paint::{self, HitId};
use crate::region::Region;
const LEFT_FILL: u32 = 0xFF00_0000; const RIGHT_FILL: u32 = 0xFFFF_FFFF; const LEFT_DISABLED: u32 = 0xFF00_0000 | (0x00FF_FFFF ^ 0x005A_5A5A); const RIGHT_DISABLED: u32 = 0xFF00_0000 | (0x00FF_FFFF ^ 0x003C_3C3C); const HANDLE_FILL: u32 = 0xFF00_0000; const HANDLE_DISABLED: u32 = 0xFF00_0000 | (0x00FF_FFFF ^ 0x005A_5A5A);
pub struct Slider {
hit_id: HitId,
pub center_x: Coord,
pub center_y: Coord,
pub width: Coord,
pub height: Coord,
value: f32,
focused: bool,
hovered: bool,
enabled: bool,
change_counter: u32,
last_seen_change_counter: u32,
last_painted_bbox: Option<PixelRect>,
last_painted_value: f32,
last_painted_enabled: bool,
last_painted_focused: bool,
}
impl Slider {
pub fn new(
hit_counter: &mut HitId,
center_x: Coord,
center_y: Coord,
width: Coord,
height: Coord,
value: f32,
) -> Self {
Self {
hit_id: crate::host::widget::next_id(hit_counter),
center_x,
center_y,
width,
height,
value: value.clamp(0.0, 1.0),
focused: false,
hovered: false,
enabled: true,
change_counter: 0,
last_seen_change_counter: 0,
last_painted_bbox: None,
last_painted_value: -1.0,
last_painted_enabled: true,
last_painted_focused: false,
}
}
pub fn hit_id(&self) -> HitId {
self.hit_id
}
pub fn value(&self) -> f32 {
self.value
}
pub fn is_focused(&self) -> bool {
self.focused
}
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn set_value(&mut self, value: f32) {
let v = value.clamp(0.0, 1.0);
if v != self.value {
self.value = v;
self.change_counter = self.change_counter.wrapping_add(1);
}
}
pub fn set_value_from_x(&mut self, x: Coord) {
if !self.enabled {
return;
}
let left = self.center_x - self.width * 0.5;
self.set_value(((x - left) / self.width.max(1e-6)) as f32);
}
pub fn take_change(&mut self) -> bool {
if self.change_counter != self.last_seen_change_counter {
self.last_seen_change_counter = self.change_counter;
true
} else {
false
}
}
pub fn set_focused(&mut self, focused: bool) {
self.focused = focused;
}
pub fn set_hovered(&mut self, hovered: bool) {
if !self.enabled {
return;
}
self.hovered = hovered;
}
pub fn set_enabled(&mut self, enabled: bool) {
if enabled == self.enabled {
return;
}
self.enabled = enabled;
if !enabled {
self.focused = false;
self.hovered = false;
}
}
pub fn set_rect(&mut self, center_x: Coord, center_y: Coord, width: Coord, height: Coord) {
self.center_x = center_x;
self.center_y = center_y;
self.width = width;
self.height = height;
}
pub fn bbox(&self) -> Region {
Region::new(
self.center_x - self.width * 0.5,
self.center_y - self.height * 0.5,
self.width,
self.height,
)
}
pub fn damage_rect(&self, viewport_w: usize, viewport_h: usize) -> Option<PixelRect> {
let dirty = self.value != self.last_painted_value
|| self.enabled != self.last_painted_enabled
|| self.focused != self.last_painted_focused;
if !dirty && self.last_painted_bbox.is_some() {
return None;
}
let current =
crate::widgets::textbox::region_to_pixelrect(self.bbox(), viewport_w, viewport_h);
Some(self.last_painted_bbox.map_or(current, |p| p.union(current)))
}
pub fn render_content_into(
&mut self,
canvas: &mut crate::canvas::Canvas,
hit_map: Option<&mut [HitId]>,
hit_id: HitId,
) {
let band_x = (self.center_x - self.width * 0.5) as isize;
let band_y = (self.center_y - self.height * 0.5) as isize;
let band_w = self.width as isize;
let band_h = self.height as isize;
if band_w <= 0 || band_h <= 0 {
return;
}
let (left_fill, right_fill, handle_fill) = if self.enabled {
(LEFT_FILL, RIGHT_FILL, HANDLE_FILL)
} else {
(LEFT_DISABLED, RIGHT_DISABLED, HANDLE_DISABLED)
};
let track_h = (band_h / 4).max(2);
let track_y = band_y + (band_h - track_h) / 2;
let handle_x = band_x + (self.value as Coord * self.width) as isize;
let radius = (band_h / 2 - 1).max(2);
paint::circle_filled(
canvas,
handle_x,
band_y + band_h / 2,
radius,
handle_fill,
None,
None,
);
paint::fill_rect(
canvas,
band_x,
track_y,
handle_x - band_x,
track_h,
left_fill,
None,
None,
);
paint::fill_rect(
canvas,
handle_x,
track_y,
band_x + band_w - handle_x,
track_h,
right_fill,
None,
None,
);
if let Some(map) = hit_map {
let vw = canvas.width;
let vh = canvas.height;
let x0 = band_x.max(0) as usize;
let y0 = band_y.max(0) as usize;
let x1 = ((band_x + band_w).max(0) as usize).min(vw);
let y1 = ((band_y + band_h).max(0) as usize).min(vh);
for row in y0..y1 {
for col in x0..x1 {
map[row * vw + col] = hit_id;
}
}
}
let vw = canvas.width;
let vh = canvas.height;
self.last_painted_bbox = Some(crate::widgets::textbox::region_to_pixelrect(
self.bbox(),
vw,
vh,
));
self.last_painted_value = self.value;
self.last_painted_enabled = self.enabled;
self.last_painted_focused = self.focused;
}
}
mod widget_impls {
use super::Slider;
use crate::coord::Coord;
use crate::event::{ElementState, Key as FKey, KeyEvent, ModifiersState, NamedKey};
use crate::host::widget::{Click, Focus, Hover, Key, PaintCtx, Widget};
use crate::paint::HitId;
use crate::text::TextRenderer;
impl Widget for Slider {
fn id(&self) -> HitId {
self.hit_id()
}
fn paint(&mut self, _ctx: &mut PaintCtx<'_, '_>) {
}
fn click(&mut self) -> Option<&mut dyn Click> {
self.enabled.then_some(self as &mut dyn Click)
}
fn key(&mut self) -> Option<&mut dyn Key> {
self.enabled.then_some(self as &mut dyn Key)
}
fn focus(&mut self) -> Option<&mut dyn Focus> {
self.enabled.then_some(self as &mut dyn Focus)
}
fn hover(&mut self) -> Option<&mut dyn Hover> {
self.enabled.then_some(self as &mut dyn Hover)
}
}
impl Click for Slider {
fn on_click(
&mut self,
x: Coord,
_y: Coord,
_mods: ModifiersState,
) -> crate::host::EventResponse {
self.set_value_from_x(x);
crate::host::EventResponse::Handled
}
}
impl Key for Slider {
fn on_key(
&mut self,
kev: &KeyEvent,
_mods: ModifiersState,
_text: &mut TextRenderer,
) -> crate::host::EventResponse {
if kev.state != ElementState::Pressed {
return crate::host::EventResponse::Pass;
}
const STEP: f32 = 1.0 / 64.0;
match &kev.logical_key {
FKey::Named(NamedKey::ArrowLeft) => {
self.set_value(self.value() - STEP);
crate::host::EventResponse::Handled
}
FKey::Named(NamedKey::ArrowRight) => {
self.set_value(self.value() + STEP);
crate::host::EventResponse::Handled
}
FKey::Named(NamedKey::Home) => {
self.set_value(0.0);
crate::host::EventResponse::Handled
}
FKey::Named(NamedKey::End) => {
self.set_value(1.0);
crate::host::EventResponse::Handled
}
_ => crate::host::EventResponse::Pass,
}
}
}
impl Focus for Slider {
fn set_focused(&mut self, focused: bool) {
Slider::set_focused(self, focused);
}
fn focus_bbox(&self) -> Option<crate::canvas::PixelRect> {
let r = self.bbox();
let x0 = r.x.max(0.0) as usize;
let y0 = r.y.max(0.0) as usize;
let x1 = (r.x + r.w).max(0.0) as usize;
let y1 = (r.y + r.h).max(0.0) as usize;
Some(crate::canvas::PixelRect::new(x0, y0, x1, y1))
}
}
impl Hover for Slider {
fn set_hovered(&mut self, hovered: bool) {
Slider::set_hovered(self, hovered);
}
fn tint_delta(&self) -> u32 {
0
}
}
}