Files
ARCADIA-Studio/crates/vendor/iced_widget/src/float.rs
T
sorokin.oleg.edu 9fd76aef3a Add current-draft diagnostic underlines and passive hover
Preserve raw include diagnostic provenance and derive bands from the existing shaped editor. Retain deferred tooltip paragraphs through rendering. Include source, regression, failure and native-trial evidence; corrected native acceptance remains pending.
2026-09-13 01:43:22 -07:00

433 lines
12 KiB
Rust

//! Make elements float!
use crate::core;
use crate::core::border;
use crate::core::layout;
use crate::core::mouse;
use crate::core::overlay;
use crate::core::renderer;
use crate::core::widget;
use crate::core::widget::tree;
use crate::core::{
Clipboard, Element, Event, Layout, Length, Rectangle, Shadow, Shell, Size,
Transformation, Vector, Widget,
};
/// A widget that can make its contents float over other widgets.
pub struct Float<'a, Message, Theme = crate::Theme, Renderer = crate::Renderer>
where
Theme: Catalog,
{
content: Element<'a, Message, Theme, Renderer>,
scale: f32,
translate: Option<Box<dyn Fn(Rectangle, Rectangle) -> Vector + 'a>>,
class: Theme::Class<'a>,
}
impl<'a, Message, Theme, Renderer> Float<'a, Message, Theme, Renderer>
where
Theme: Catalog,
{
/// Creates a new [`Float`] widget with the given content.
pub fn new(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Self {
Self {
content: content.into(),
scale: 1.0,
translate: None,
class: Theme::default(),
}
}
/// Sets the scale to be applied to the contents of the [`Float`].
pub fn scale(mut self, scale: f32) -> Self {
self.scale = scale;
self
}
/// Sets the translation logic to be applied to the contents of the [`Float`].
///
/// The logic takes the original (non-scaled) bounds of the contents and the
/// viewport bounds. These bounds can be useful to ensure the floating elements
/// always stay on screen.
pub fn translate(
mut self,
translate: impl Fn(Rectangle, Rectangle) -> Vector + 'a,
) -> Self {
self.translate = Some(Box::new(translate));
self
}
/// Sets the style of the [`Float`].
#[must_use]
pub fn style(mut self, style: impl Fn(&Theme) -> Style + 'a) -> Self
where
Theme::Class<'a>: From<StyleFn<'a, Theme>>,
{
self.class = (Box::new(style) as StyleFn<'a, Theme>).into();
self
}
/// Sets the style class of the [`Float`].
#[cfg(feature = "advanced")]
#[must_use]
pub fn class(mut self, class: impl Into<Theme::Class<'a>>) -> Self {
self.class = class.into();
self
}
fn is_floating(&self, bounds: Rectangle, viewport: Rectangle) -> bool {
self.scale > 1.0
|| self.translate.as_ref().is_some_and(|translate| {
translate(bounds, viewport) != Vector::ZERO
})
}
}
impl<Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Float<'_, Message, Theme, Renderer>
where
Theme: Catalog,
Renderer: core::Renderer,
{
fn tag(&self) -> tree::Tag {
self.content.as_widget().tag()
}
fn state(&self) -> tree::State {
self.content.as_widget().state()
}
fn children(&self) -> Vec<tree::Tree> {
self.content.as_widget().children()
}
fn diff(&self, tree: &mut widget::Tree) {
self.content.as_widget().diff(tree);
}
fn size(&self) -> Size<Length> {
self.content.as_widget().size()
}
fn size_hint(&self) -> Size<Length> {
self.content.as_widget().size_hint()
}
fn layout(
&mut self,
tree: &mut widget::Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content.as_widget_mut().layout(tree, renderer, limits)
}
fn update(
&mut self,
tree: &mut widget::Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) {
if self.is_floating(layout.bounds(), *viewport) {
return;
}
self.content.as_widget_mut().update(
tree, event, layout, cursor, renderer, clipboard, shell, viewport,
);
}
fn draw(
&self,
tree: &widget::Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
) {
if self.is_floating(layout.bounds(), *viewport) {
return;
}
{
let style = theme.style(&self.class);
if style.shadow.color.a > 0.0 {
renderer.fill_quad(
renderer::Quad {
bounds: layout.bounds().shrink(1.0),
shadow: style.shadow,
border: border::rounded(style.shadow_border_radius),
snap: false,
},
style.shadow.color,
);
}
}
self.content
.as_widget()
.draw(tree, renderer, theme, style, layout, cursor, viewport);
}
fn mouse_interaction(
&self,
tree: &widget::Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
if self.is_floating(layout.bounds(), *viewport) {
return mouse::Interaction::None;
}
self.content
.as_widget()
.mouse_interaction(tree, layout, cursor, viewport, renderer)
}
fn operate(
&mut self,
tree: &mut widget::Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn widget::Operation,
) {
self.content
.as_widget_mut()
.operate(tree, layout, renderer, operation);
}
fn overlay<'a>(
&'a mut self,
state: &'a mut widget::Tree,
layout: Layout<'a>,
renderer: &Renderer,
viewport: &Rectangle,
offset: Vector,
) -> Option<overlay::Element<'a, Message, Theme, Renderer>> {
let bounds = layout.bounds();
let translation = self
.translate
.as_ref()
.map(|translate| translate(bounds + offset, *viewport))
.unwrap_or(Vector::ZERO);
if self.scale > 1.0 || translation != Vector::ZERO {
let translation = translation + offset;
let transformation = Transformation::translate(
bounds.x + bounds.width / 2.0 + translation.x,
bounds.y + bounds.height / 2.0 + translation.y,
) * Transformation::scale(self.scale)
* Transformation::translate(
-bounds.x - bounds.width / 2.0,
-bounds.y - bounds.height / 2.0,
);
Some(overlay::Element::new(Box::new(Overlay {
float: self,
state,
layout,
viewport: *viewport,
transformation,
})))
} else {
self.content
.as_widget_mut()
.overlay(state, layout, renderer, viewport, offset)
}
}
}
impl<'a, Message, Theme, Renderer> From<Float<'a, Message, Theme, Renderer>>
for Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Theme: Catalog + 'a,
Renderer: core::Renderer + 'a,
{
fn from(float: Float<'a, Message, Theme, Renderer>) -> Self {
Element::new(float)
}
}
struct Overlay<'a, 'b, Message, Theme, Renderer>
where
Theme: Catalog,
{
float: &'a mut Float<'b, Message, Theme, Renderer>,
state: &'a mut widget::Tree,
layout: Layout<'a>,
viewport: Rectangle,
transformation: Transformation,
}
impl<Message, Theme, Renderer> core::Overlay<Message, Theme, Renderer>
for Overlay<'_, '_, Message, Theme, Renderer>
where
Theme: Catalog,
Renderer: core::Renderer,
{
fn layout(&mut self, _renderer: &Renderer, _bounds: Size) -> layout::Node {
let bounds = self.layout.bounds() * self.transformation;
layout::Node::new(bounds.size()).move_to(bounds.position())
}
fn update(
&mut self,
event: &Event,
_layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
) {
let inverse = self.transformation.inverse();
self.float.content.as_widget_mut().update(
self.state,
event,
self.layout,
cursor * inverse,
renderer,
clipboard,
shell,
&(self.viewport * inverse),
);
}
fn draw(
&self,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
_layout: Layout<'_>,
cursor: mouse::Cursor,
) {
let bounds = self.layout.bounds();
let inverse = self.transformation.inverse();
renderer.with_layer(self.viewport, |renderer| {
renderer.with_transformation(self.transformation, |renderer| {
{
let style = theme.style(&self.float.class);
if style.shadow.color.a > 0.0 {
renderer.fill_quad(
renderer::Quad {
bounds: bounds.shrink(1.0),
shadow: style.shadow,
border: border::rounded(
style.shadow_border_radius,
),
snap: false,
},
style.shadow.color,
);
}
}
self.float.content.as_widget().draw(
self.state,
renderer,
theme,
style,
self.layout,
cursor * inverse,
&(self.viewport * inverse),
);
});
});
}
fn mouse_interaction(
&self,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
) -> mouse::Interaction {
if !cursor.is_over(layout.bounds()) {
return mouse::Interaction::None;
}
let inverse = self.transformation.inverse();
self.float.content.as_widget().mouse_interaction(
self.state,
self.layout,
cursor * inverse,
&(self.viewport * inverse),
renderer,
)
}
fn index(&self) -> f32 {
self.float.scale * 0.5
}
fn overlay<'a>(
&'a mut self,
_layout: Layout<'_>,
renderer: &Renderer,
) -> Option<overlay::Element<'a, Message, Theme, Renderer>> {
self.float.content.as_widget_mut().overlay(
self.state,
self.layout,
renderer,
&(self.viewport * self.transformation.inverse()),
self.transformation.translation(),
)
}
}
/// The theme catalog of a [`Float`].
///
/// All themes that can be used with [`Float`]
/// must implement this trait.
pub trait Catalog {
/// The item class of the [`Catalog`].
type Class<'a>;
/// The default class produced by the [`Catalog`].
fn default<'a>() -> Self::Class<'a>;
/// The [`Style`] of a class with the given status.
fn style(&self, class: &Self::Class<'_>) -> Style;
}
/// A styling function for a [`Float`].
pub type StyleFn<'a, Theme> = Box<dyn Fn(&Theme) -> Style + 'a>;
impl Catalog for crate::Theme {
type Class<'a> = StyleFn<'a, Self>;
fn default<'a>() -> Self::Class<'a> {
Box::new(|_| Style::default())
}
fn style(&self, class: &Self::Class<'_>) -> Style {
class(self)
}
}
/// The style of a [`Float`].
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub struct Style {
/// The [`Shadow`] of the [`Float`].
pub shadow: Shadow,
/// The border radius of the shadow.
pub shadow_border_radius: border::Radius,
}