Files
mspaint/src/image_button.rs
2026-02-24 22:20:06 +08:00

198 lines
5.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use iced::advanced::layout::{self, Layout};
use iced::advanced::widget::{self, Widget};
use iced::advanced::{Clipboard, renderer};
use iced::mouse;
use iced::{Element, Event, Length, Rectangle, Size};
// We need the image renderer trait
use iced::advanced::image as img;
// ---------- Widget struct ----------
/// A button that displays one image when idle and another when pressed.
pub struct ImageButton<Handle, Message> {
normal: Handle,
pressed: Handle,
width: Length,
height: Length,
on_press: Option<Message>,
is_pressed: bool,
}
pub fn image_button<Handle, Message>(
normal: impl Into<Handle>,
pressed: impl Into<Handle>,
is_pressed: bool,
) -> ImageButton<Handle, Message> {
ImageButton::new(normal, pressed, is_pressed)
}
impl<Handle, Message> ImageButton<Handle, Message> {
/// Create a new [`ImageButton`].
///
/// * `normal` image shown in the default / hover state
/// * `pressed` image shown while the left mouse button is held
pub fn new(normal: impl Into<Handle>, pressed: impl Into<Handle>, is_pressed: bool) -> Self {
Self {
normal: normal.into(),
pressed: pressed.into(),
width: Length::Shrink,
height: Length::Shrink,
on_press: None,
is_pressed,
}
}
/// The message to emit when the button is clicked (press + release).
pub fn on_press(mut self, message: Message) -> Self {
self.on_press = Some(message);
self
}
/// Override the widget width.
pub fn width(mut self, width: impl Into<Length>) -> Self {
self.width = width.into();
self
}
/// Override the widget height.
pub fn height(mut self, height: impl Into<Length>) -> Self {
self.height = height.into();
self
}
}
// ---------- Widget impl ----------
impl<Message, Theme, Renderer, Handle> Widget<Message, Theme, Renderer>
for ImageButton<Handle, Message>
where
Renderer: img::Renderer<Handle = Handle>,
Handle: Clone,
Message: Clone,
{
// --- Size ---
fn size(&self) -> Size<Length> {
Size {
width: self.width,
height: self.height,
}
}
// --- Layout ---
fn layout(
&mut self,
_tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
// Use the natural dimensions of the normal image if the renderer
// can measure it; otherwise fall back to the limits maximum.
let size = renderer
.measure_image(&self.normal)
.map(|s| Size::new(s.width as f32, s.height as f32))
.unwrap_or_else(|| limits.max());
let size = limits.resolve(self.width, self.height, size);
layout::Node::new(size)
}
// --- Events ---
fn update(
&mut self,
_tree: &mut widget::Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
_renderer: &Renderer,
_clipboard: &mut dyn Clipboard,
shell: &mut iced::advanced::Shell<'_, Message>,
_viewport: &Rectangle,
) {
let bounds = layout.bounds();
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
if cursor.is_over(bounds) {
shell.capture_event();
shell.request_redraw();
if let Some(on_press) = self.on_press.clone() {
shell.publish(on_press);
}
}
}
_ => {}
}
}
// --- Cursor ---
fn mouse_interaction(
&self,
_tree: &widget::Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
if cursor.is_over(layout.bounds()) && self.on_press.is_some() {
mouse::Interaction::Pointer
} else {
mouse::Interaction::None
}
}
// --- Draw ---
fn draw(
&self,
_tree: &widget::Tree,
renderer: &mut Renderer,
_theme: &Theme,
_style: &renderer::Style,
layout: Layout<'_>,
_cursor: mouse::Cursor,
_viewport: &Rectangle,
) {
let bounds = layout.bounds();
// Pick the correct image handle.
let handle = if self.is_pressed {
&self.pressed
} else {
&self.normal
};
renderer.draw_image(
img::Image {
handle: handle.clone(),
border_radius: 0.0.into(),
filter_method: img::FilterMethod::Linear,
rotation: iced::Radians(0.0),
opacity: 1.0,
snap: false,
},
bounds, // drawing bounds
bounds, // clip bounds
);
}
}
// ---------- Into<Element> ----------
impl<'a, Message, Theme, Renderer, Handle> From<ImageButton<Handle, Message>>
for Element<'a, Message, Theme, Renderer>
where
Renderer: img::Renderer<Handle = Handle> + 'a,
Handle: Clone + 'a,
Message: Clone + 'a,
Theme: 'a,
{
fn from(widget: ImageButton<Handle, Message>) -> Self {
Self::new(widget)
}
}