pub use hover_container::HoverContainer;
pub mod hover_container {
use iced::{Alignment, Color, Length, Point, Rectangle};
use iced_native::layout::{Limits, Node};
use iced_native::renderer::Quad;
use iced_native::widget::{Operation, Tree};
use iced_native::{overlay, Clipboard, Element, Event, Layout, Padding, Shell, Widget};
pub struct HoverContainer<'a, Message, Renderer: iced_native::Renderer> {
padding: Padding,
content: Element<'a, Message, Renderer>,
width: Length,
height: Length,
max_width: u32,
max_height: u32,
horizontal_alignment: Alignment,
vertical_alignment: Alignment,
style: Box<dyn self::style::StyleSheet>,
}
impl<'a, Message, Renderer> HoverContainer<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
{
pub fn new<T>(content: T) -> Self
where
T: Into<Element<'a, Message, Renderer>>,
{
HoverContainer {
padding: Padding::ZERO,
width: Length::Shrink,
height: Length::Shrink,
max_width: u32::MAX,
max_height: u32::MAX,
horizontal_alignment: Alignment::Start,
vertical_alignment: Alignment::Start,
style: Box::<crate::style::HoverContainer>::default(),
content: content.into(),
}
}
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
self.padding = padding.into();
self
}
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
pub fn max_width(mut self, max_width: u32) -> Self {
self.max_width = max_width;
self
}
pub fn max_height(mut self, max_height: u32) -> Self {
self.max_height = max_height;
self
}
pub fn align_x(mut self, alignment: Alignment) -> Self {
self.horizontal_alignment = alignment;
self
}
pub fn align_y(mut self, alignment: Alignment) -> Self {
self.vertical_alignment = alignment;
self
}
pub fn center_x(mut self) -> Self {
self.horizontal_alignment = Alignment::Center;
self
}
pub fn center_y(mut self) -> Self {
self.vertical_alignment = Alignment::Center;
self
}
pub fn style(mut self, stylesheet: impl Into<Box<dyn self::style::StyleSheet>>) -> Self {
self.style = stylesheet.into();
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer> for HoverContainer<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
{
fn width(&self) -> Length {
self.width
}
fn height(&self) -> Length {
self.height
}
fn layout(&self, renderer: &Renderer, limits: &Limits) -> Node {
let limits = limits
.loose()
.max_width(self.max_width as f32)
.max_height(self.max_height as f32)
.width(self.width)
.height(self.height)
.pad(self.padding);
let mut content = self.content.as_widget().layout(renderer, &limits.loose());
let size = limits.resolve(content.size());
content.move_to(Point::new(
self.padding.left.into(),
self.padding.top.into(),
));
content.align(self.horizontal_alignment, self.vertical_alignment, size);
Node::with_children(size.pad(self.padding), vec![content])
}
fn draw(
&self,
state: &iced_native::widget::Tree,
renderer: &mut Renderer,
theme: &Renderer::Theme,
style: &iced_native::renderer::Style,
layout: Layout<'_>,
cursor_position: Point,
viewport: &Rectangle,
) {
let is_hovered = layout.bounds().contains(cursor_position);
let container_style = if is_hovered {
self.style.hovered()
} else {
self.style.style()
};
renderer.fill_quad(
Quad {
bounds: layout.bounds(),
border_radius: iced_native::renderer::BorderRadius::from(
container_style.border_radius,
),
border_color: container_style.border_color,
border_width: container_style.border_width,
},
container_style
.background
.unwrap_or_else(|| Color::TRANSPARENT.into()),
);
self.content.as_widget().draw(
&state.children[0],
renderer,
theme,
&iced_native::renderer::Style {
text_color: container_style.text_color.unwrap_or(style.text_color),
},
layout.children().next().unwrap(),
cursor_position,
viewport,
);
}
fn diff(&self, tree: &mut Tree) {
tree.diff_children(std::slice::from_ref(&self.content))
}
fn children(&self) -> Vec<Tree> {
vec![Tree::new(&self.content)]
}
fn operate(
&self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn Operation<Message>,
) {
operation.container(None, &mut |operation| {
self.content.as_widget().operate(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,
operation,
);
});
}
fn overlay<'b>(
&'b mut self,
state: &'b mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
self.content.as_widget_mut().overlay(
&mut state.children[0],
layout.children().next().unwrap(),
renderer,
)
}
fn on_event(
&mut self,
state: &mut iced_native::widget::Tree,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) -> iced_native::event::Status {
self.content.as_widget_mut().on_event(
&mut state.children[0],
event,
layout.children().next().unwrap(),
cursor_position,
renderer,
clipboard,
shell,
)
}
}
impl<'a, Message, Renderer> From<HoverContainer<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'a + iced_native::Renderer,
Message: 'a,
{
fn from(
container: HoverContainer<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(container)
}
}
pub mod style {
use iced::{Background, Color};
#[derive(Debug, Clone)]
pub struct Style {
pub text_color: Option<Color>,
pub background: Option<Background>,
pub border_radius: f32,
pub border_width: f32,
pub border_color: Color,
}
impl std::default::Default for Style {
fn default() -> Self {
Self {
text_color: None,
background: None,
border_radius: 0.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
}
}
}
pub trait StyleSheet {
fn style(&self) -> Style;
fn hovered(&self) -> Style;
}
struct Default;
impl StyleSheet for Default {
fn style(&self) -> Style {
Style {
text_color: None,
background: None,
border_radius: 0.0,
border_width: 0.0,
border_color: Color::TRANSPARENT,
}
}
fn hovered(&self) -> Style {
self.style()
}
}
impl std::default::Default for Box<dyn StyleSheet> {
fn default() -> Self {
Box::new(Default)
}
}
impl<T> From<T> for Box<dyn StyleSheet>
where
T: 'static + StyleSheet,
{
fn from(style: T) -> Self {
Box::new(style)
}
}
}
}