mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add a tooltip to the char select screen to test
This commit is contained in:
parent
5c85a29111
commit
d8854e9300
@ -5,11 +5,14 @@ use crate::{
|
|||||||
self,
|
self,
|
||||||
fonts::IcedFonts as Fonts,
|
fonts::IcedFonts as Fonts,
|
||||||
ice::{
|
ice::{
|
||||||
component::neat_button,
|
component::{
|
||||||
|
neat_button,
|
||||||
|
tooltip::{self, WithTooltip},
|
||||||
|
},
|
||||||
style,
|
style,
|
||||||
widget::{
|
widget::{
|
||||||
mouse_detector, AspectRatioContainer, BackgroundContainer, Image, MouseDetector,
|
mouse_detector, AspectRatioContainer, BackgroundContainer, Image, MouseDetector,
|
||||||
Overlay, Padding,
|
Overlay, Padding, TooltipManager,
|
||||||
},
|
},
|
||||||
Element, IcedRenderer, IcedUi as Ui,
|
Element, IcedRenderer, IcedUi as Ui,
|
||||||
},
|
},
|
||||||
@ -36,8 +39,11 @@ use vek::Rgba;
|
|||||||
|
|
||||||
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
|
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
|
||||||
pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
|
pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
|
||||||
|
pub const TOOLTIP_BACK_COLOR: Rgba<u8> = Rgba::new(20, 18, 10, 255);
|
||||||
const FILL_FRAC_ONE: f32 = 0.77;
|
const FILL_FRAC_ONE: f32 = 0.77;
|
||||||
const FILL_FRAC_TWO: f32 = 0.60;
|
const FILL_FRAC_TWO: f32 = 0.60;
|
||||||
|
const TOOLTIP_HOVER_DUR: std::time::Duration = std::time::Duration::from_millis(500);
|
||||||
|
const TOOLTIP_FADE_DUR: std::time::Duration = std::time::Duration::from_millis(500);
|
||||||
|
|
||||||
const STARTER_HAMMER: &str = "common.items.weapons.hammer.starter_hammer";
|
const STARTER_HAMMER: &str = "common.items.weapons.hammer.starter_hammer";
|
||||||
const STARTER_BOW: &str = "common.items.weapons.bow.starter_bow";
|
const STARTER_BOW: &str = "common.items.weapons.bow.starter_bow";
|
||||||
@ -104,19 +110,12 @@ image_ids_ice! {
|
|||||||
button: "voxygen.element.buttons.button",
|
button: "voxygen.element.buttons.button",
|
||||||
button_hover: "voxygen.element.buttons.button_hover",
|
button_hover: "voxygen.element.buttons.button_hover",
|
||||||
button_press: "voxygen.element.buttons.button_press",
|
button_press: "voxygen.element.buttons.button_press",
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: do rotation in widget renderer
|
// Tooltips
|
||||||
/*rotation_image_ids! {
|
tt_edge: "voxygen/element/frames/tt_test_edge",
|
||||||
pub struct ImgsRot {
|
|
||||||
<VoxelGraphic>
|
|
||||||
|
|
||||||
// Tooltip Test
|
|
||||||
tt_side: "voxygen/element/frames/tt_test_edge",
|
|
||||||
tt_corner: "voxygen/element/frames/tt_test_corner_tr",
|
tt_corner: "voxygen/element/frames/tt_test_corner_tr",
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
Logout,
|
Logout,
|
||||||
@ -235,6 +234,7 @@ struct Controls {
|
|||||||
// Alpha disclaimer
|
// Alpha disclaimer
|
||||||
alpha: String,
|
alpha: String,
|
||||||
|
|
||||||
|
tooltip_manager: TooltipManager,
|
||||||
// Zone for rotating the character with the mouse
|
// Zone for rotating the character with the mouse
|
||||||
mouse_detector: mouse_detector::State,
|
mouse_detector: mouse_detector::State,
|
||||||
// enter: bool,
|
// enter: bool,
|
||||||
@ -272,6 +272,7 @@ impl Controls {
|
|||||||
version,
|
version,
|
||||||
alpha,
|
alpha,
|
||||||
|
|
||||||
|
tooltip_manager: TooltipManager::new(TOOLTIP_HOVER_DUR, TOOLTIP_FADE_DUR),
|
||||||
mouse_detector: Default::default(),
|
mouse_detector: Default::default(),
|
||||||
mode: Mode::select(),
|
mode: Mode::select(),
|
||||||
}
|
}
|
||||||
@ -279,12 +280,16 @@ impl Controls {
|
|||||||
|
|
||||||
fn view(&mut self, settings: &Settings, client: &Client) -> Element<Message> {
|
fn view(&mut self, settings: &Settings, client: &Client) -> Element<Message> {
|
||||||
// TODO: use font scale thing for text size (use on button size for buttons with
|
// TODO: use font scale thing for text size (use on button size for buttons with
|
||||||
// text) TODO: if enter key pressed and character is selected then enter
|
// text)
|
||||||
// the world TODO: tooltip widget
|
// TODO: if enter key pressed and character is selected then enter the world
|
||||||
|
|
||||||
|
// Maintain tooltip manager
|
||||||
|
self.tooltip_manager.maintain();
|
||||||
|
|
||||||
let imgs = &self.imgs;
|
let imgs = &self.imgs;
|
||||||
let fonts = &self.fonts;
|
let fonts = &self.fonts;
|
||||||
let i18n = &self.i18n;
|
let i18n = &self.i18n;
|
||||||
|
let tooltip_manager = &self.tooltip_manager;
|
||||||
|
|
||||||
let button_style = style::button::Style::new(imgs.button)
|
let button_style = style::button::Style::new(imgs.button)
|
||||||
.hover_image(imgs.button_hover)
|
.hover_image(imgs.button_hover)
|
||||||
@ -292,6 +297,16 @@ impl Controls {
|
|||||||
.text_color(TEXT_COLOR)
|
.text_color(TEXT_COLOR)
|
||||||
.disabled_text_color(DISABLED_TEXT_COLOR);
|
.disabled_text_color(DISABLED_TEXT_COLOR);
|
||||||
|
|
||||||
|
let tooltip_style = tooltip::Style {
|
||||||
|
container: style::container::Style::color_with_image_border(
|
||||||
|
TOOLTIP_BACK_COLOR,
|
||||||
|
imgs.tt_corner,
|
||||||
|
imgs.tt_edge,
|
||||||
|
),
|
||||||
|
text_color: TEXT_COLOR,
|
||||||
|
text_size: self.fonts.cyri.scale(15),
|
||||||
|
};
|
||||||
|
|
||||||
let version = iced::Text::new(&self.version)
|
let version = iced::Text::new(&self.version)
|
||||||
.size(self.fonts.cyri.scale(15))
|
.size(self.fonts.cyri.scale(15))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
@ -382,11 +397,21 @@ impl Controls {
|
|||||||
Button::new(
|
Button::new(
|
||||||
delete_button,
|
delete_button,
|
||||||
Column::with_children(vec![
|
Column::with_children(vec![
|
||||||
Text::new("Hi").into(),
|
Text::new(&character.character.alias).into(),
|
||||||
Text::new("Hi").into(),
|
// TODO: only construct string once when characters are
|
||||||
Text::new("Hi").into(),
|
// loaded
|
||||||
|
Text::new(
|
||||||
|
i18n.get("char_selection.level_fmt").replace(
|
||||||
|
"{level_nb}",
|
||||||
|
&character.level.to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
|
Text::new(i18n.get("char_selection.uncanny_valley"))
|
||||||
|
.into(),
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
.padding(8)
|
||||||
.style(
|
.style(
|
||||||
style::button::Style::new(imgs.selection)
|
style::button::Style::new(imgs.selection)
|
||||||
.hover_image(imgs.selection_hover)
|
.hover_image(imgs.selection_hover)
|
||||||
@ -394,7 +419,16 @@ impl Controls {
|
|||||||
)
|
)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
.on_press(Message::Select(i)),
|
.on_press(Message::Select(i))
|
||||||
|
.with_tooltip(
|
||||||
|
tooltip_manager,
|
||||||
|
move || {
|
||||||
|
tooltip::text(
|
||||||
|
i18n.get("char_selection.delete_permanently"),
|
||||||
|
tooltip_style,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.ratio_of_image(imgs.selection),
|
.ratio_of_image(imgs.selection),
|
||||||
)
|
)
|
||||||
@ -516,7 +550,7 @@ impl Controls {
|
|||||||
let over: Element<_> = match info_content {
|
let over: Element<_> = match info_content {
|
||||||
InfoContent::Deletion(_) => Container::new(
|
InfoContent::Deletion(_) => Container::new(
|
||||||
Column::with_children(vec![
|
Column::with_children(vec![
|
||||||
Text::new(self.i18n.get("char_selection.delete_permanently"))
|
Text::new(i18n.get("char_selection.delete_permanently"))
|
||||||
.size(fonts.cyri.scale(24))
|
.size(fonts.cyri.scale(24))
|
||||||
.into(),
|
.into(),
|
||||||
Row::with_children(vec![
|
Row::with_children(vec![
|
||||||
|
@ -28,7 +28,7 @@ pub enum Graphic {
|
|||||||
Blank,
|
Blank,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Rotation {
|
pub enum Rotation {
|
||||||
None,
|
None,
|
||||||
Cw90,
|
Cw90,
|
||||||
|
@ -1,2 +1,6 @@
|
|||||||
|
/// Various composable helpers for making iced ui's
|
||||||
pub mod neat_button;
|
pub mod neat_button;
|
||||||
|
pub mod tooltip;
|
||||||
|
|
||||||
pub use neat_button::neat_button;
|
pub use neat_button::neat_button;
|
||||||
|
//pub use tooltip::WithTooltip;
|
||||||
|
40
voxygen/src/ui/ice/component/tooltip.rs
Normal file
40
voxygen/src/ui/ice/component/tooltip.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use crate::ui::ice as ui;
|
||||||
|
use iced::{Container, Element, Text};
|
||||||
|
use ui::{
|
||||||
|
style,
|
||||||
|
widget::{Tooltip, TooltipManager},
|
||||||
|
};
|
||||||
|
|
||||||
|
// :( all tooltips have to copy because this is needed outside the function
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Style {
|
||||||
|
pub container: style::container::Style,
|
||||||
|
pub text_color: iced::Color,
|
||||||
|
pub text_size: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tooltip that is just text
|
||||||
|
pub fn text<'a, M: 'a>(text: &'a str, style: Style) -> Element<'a, M, ui::IcedRenderer> {
|
||||||
|
Container::new(
|
||||||
|
Text::new(text)
|
||||||
|
.color(style.text_color)
|
||||||
|
.size(style.text_size),
|
||||||
|
)
|
||||||
|
.style(style.container)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait WithTooltip<'a, M, R: iced::Renderer> {
|
||||||
|
fn with_tooltip<H>(self, manager: &'a TooltipManager, hover_content: H) -> Tooltip<'a, M, R>
|
||||||
|
where
|
||||||
|
H: 'a + FnMut() -> Element<'a, M, R>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, M, R: iced::Renderer, E: Into<Element<'a, M, R>>> WithTooltip<'a, M, R> for E {
|
||||||
|
fn with_tooltip<H>(self, manager: &'a TooltipManager, hover_content: H) -> Tooltip<'a, M, R>
|
||||||
|
where
|
||||||
|
H: 'a + FnMut() -> Element<'a, M, R>,
|
||||||
|
{
|
||||||
|
Tooltip::new(self, hover_content, manager)
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
use crate::ui::{graphic, ice::widget::image};
|
use crate::ui::{graphic, ice::widget::image};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Primitive {
|
pub enum Primitive {
|
||||||
// Allocation :(
|
// Allocation :(
|
||||||
Group {
|
Group {
|
||||||
|
@ -2,6 +2,7 @@ use super::super::super::widget::image;
|
|||||||
use vek::Rgba;
|
use vek::Rgba;
|
||||||
|
|
||||||
/// Container Border
|
/// Container Border
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub enum Border {
|
pub enum Border {
|
||||||
DoubleCornerless {
|
DoubleCornerless {
|
||||||
inner: Rgba<u8>,
|
inner: Rgba<u8>,
|
||||||
@ -15,6 +16,7 @@ pub enum Border {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Background of the container
|
/// Background of the container
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub enum Style {
|
pub enum Style {
|
||||||
Image(image::Handle, Rgba<u8>),
|
Image(image::Handle, Rgba<u8>),
|
||||||
Color(Rgba<u8>, Border),
|
Color(Rgba<u8>, Border),
|
||||||
|
@ -160,6 +160,10 @@ where
|
|||||||
|
|
||||||
self.content.hash_layout(state);
|
self.content.hash_layout(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlay(&mut self, layout: Layout<'_>) -> Option<iced::overlay::Element<'_, M, R>> {
|
||||||
|
self.content.overlay(layout.children().next().unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Renderer: iced::Renderer {
|
pub trait Renderer: iced::Renderer {
|
||||||
|
@ -319,6 +319,10 @@ where
|
|||||||
|
|
||||||
self.content.hash_layout(state);
|
self.content.hash_layout(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlay(&mut self, layout: Layout<'_>) -> Option<iced::overlay::Element<'_, M, R>> {
|
||||||
|
self.content.overlay(layout.children().next().unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Renderer: iced::Renderer {
|
pub trait Renderer: iced::Renderer {
|
||||||
|
@ -195,6 +195,13 @@ where
|
|||||||
self.over.hash_layout(state);
|
self.over.hash_layout(state);
|
||||||
self.under.hash_layout(state);
|
self.under.hash_layout(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlay(&mut self, layout: Layout<'_>) -> Option<iced::overlay::Element<'_, M, R>> {
|
||||||
|
let mut children = layout.children();
|
||||||
|
let (over, under) = (&mut self.over, &mut self.under);
|
||||||
|
over.overlay(children.next().unwrap())
|
||||||
|
.or_else(move || under.overlay(children.next().unwrap()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Renderer: iced::Renderer {
|
pub trait Renderer: iced::Renderer {
|
||||||
|
@ -72,6 +72,14 @@ where
|
|||||||
.iter()
|
.iter()
|
||||||
.for_each(|child| child.hash_layout(state));
|
.for_each(|child| child.hash_layout(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn overlay(&mut self, layout: Layout<'_>) -> Option<iced::overlay::Element<'_, M, R>> {
|
||||||
|
self.children
|
||||||
|
.iter_mut()
|
||||||
|
.zip(layout.children())
|
||||||
|
.filter_map(|(child, layout)| child.overlay(layout))
|
||||||
|
.next()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Renderer: iced::Renderer {
|
pub trait Renderer: iced::Renderer {
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
use iced::{
|
use iced::{
|
||||||
layout, mouse, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle,
|
layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size, Widget,
|
||||||
Size, Widget,
|
};
|
||||||
|
use std::{
|
||||||
|
hash::Hash,
|
||||||
|
sync::Mutex,
|
||||||
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use std::time::Instant;
|
|
||||||
use std::time::Duration;
|
|
||||||
use std::hash::Hash;
|
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
struct Hover {
|
struct Hover {
|
||||||
start: Instant,
|
start: Instant,
|
||||||
aabr: Aabr<i32>,
|
aabr: Aabr<i32>,
|
||||||
@ -22,26 +23,29 @@ impl Hover {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
struct Show {
|
struct Show {
|
||||||
hover_pos: Vec2<i32>,
|
hover_pos: Vec2<i32>,
|
||||||
aabr: Aabr<i32>,
|
aabr: Aabr<i32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum State {
|
enum State {
|
||||||
Idle,
|
Idle,
|
||||||
Start(Hover),
|
Start(Hover),
|
||||||
Showing(Show),
|
Showing(Show),
|
||||||
Fading(Instant, Show, Option<Hover>)
|
Fading(Instant, Show, Option<Hover>),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reports which widget the mouse is over
|
// Reports which widget the mouse is over
|
||||||
pub struct Update((Aabr<i32>, Vec2<i32>));
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
struct Update((Aabr<i32>, Vec2<i32>));
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
// TODO: consider moving all this state into the Renderer
|
||||||
pub struct TooltipManager {
|
pub struct TooltipManager {
|
||||||
state: State,
|
state: State,
|
||||||
hover_widget: Option<Aabr<i32>>,
|
update: Mutex<Option<Update>>,
|
||||||
hover_pos: Vec2<i32>,
|
hover_pos: Vec2<i32>,
|
||||||
// How long before a tooltip is displayed when hovering
|
// How long before a tooltip is displayed when hovering
|
||||||
hover_dur: Duration,
|
hover_dur: Duration,
|
||||||
@ -53,27 +57,35 @@ impl TooltipManager {
|
|||||||
pub fn new(hover_dur: Duration, fade_dur: Duration) -> Self {
|
pub fn new(hover_dur: Duration, fade_dur: Duration) -> Self {
|
||||||
Self {
|
Self {
|
||||||
state: State::Idle,
|
state: State::Idle,
|
||||||
hover_widget: None,
|
update: Mutex::new(None),
|
||||||
hover_pos: Default::default(),
|
hover_pos: Default::default(),
|
||||||
hover_dur,
|
hover_dur,
|
||||||
fade_dur,
|
fade_dur,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Call this at the top of your view function or for minimum latency at the end of message
|
|
||||||
/// handling
|
/// Call this at the top of your view function or for minimum latency at the
|
||||||
/// Only call this once per frame as it assumes no updates being received between calls means
|
/// end of message handling
|
||||||
/// that there is no tooltipped widget currently being hovered
|
/// that there is no tooltipped widget currently being hovered
|
||||||
pub fn maintain(&mut self) {
|
pub fn maintain(&mut self) {
|
||||||
|
let update = self.update.get_mut().unwrap().take();
|
||||||
// Handle changes based on pointer moving
|
// Handle changes based on pointer moving
|
||||||
self.state = if let Some(aabr) = self.hover_widget.take() {
|
self.state = if let Some(Update((aabr, hover_pos))) = update {
|
||||||
|
self.hover_pos = hover_pos;
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Idle => State::Start(Hover::start(aabr)),
|
State::Idle => State::Start(Hover::start(aabr)),
|
||||||
State::Start(hover) if hover.aabr != aabr => State::Start(Hover::start(aabr)),
|
State::Start(hover) if hover.aabr != aabr => State::Start(Hover::start(aabr)),
|
||||||
State::Start(hover) => State::Start(hover),
|
State::Start(hover) => State::Start(hover),
|
||||||
State::Showing(show) if show.aabr != aabr => State::Fading(Instant::now(), show, Some(Hover::start(aabr))),
|
State::Showing(show) if show.aabr != aabr => {
|
||||||
|
State::Fading(Instant::now(), show, Some(Hover::start(aabr)))
|
||||||
|
},
|
||||||
State::Showing(show) => State::Showing(show),
|
State::Showing(show) => State::Showing(show),
|
||||||
State::Fading(start, show, Some(hover)) if hover.aabr == aabr => State::Fading(start, show, Some(hover)),
|
State::Fading(start, show, Some(hover)) if hover.aabr == aabr => {
|
||||||
State::Fading(start, show, _) => State::Fading(start, show, Some(Hover::start(aabr))),
|
State::Fading(start, show, Some(hover))
|
||||||
|
},
|
||||||
|
State::Fading(start, show, _) => {
|
||||||
|
State::Fading(start, show, Some(Hover::start(aabr)))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self.state {
|
match self.state {
|
||||||
@ -85,22 +97,29 @@ impl TooltipManager {
|
|||||||
|
|
||||||
// Handle temporal changes
|
// Handle temporal changes
|
||||||
self.state = match self.state {
|
self.state = match self.state {
|
||||||
State::Start(Hover { start, aabr}) | State::Fading(_, _, Some(Hover { start, aabr })) if start.elapsed() >= self.hover_dur =>
|
State::Start(Hover { start, aabr })
|
||||||
State::Showing(Show { aabr, hover_pos: self.hover_pos }),
|
| State::Fading(_, _, Some(Hover { start, aabr }))
|
||||||
|
if start.elapsed() >= self.hover_dur =>
|
||||||
|
{
|
||||||
|
State::Showing(Show {
|
||||||
|
aabr,
|
||||||
|
hover_pos: self.hover_pos,
|
||||||
|
})
|
||||||
|
},
|
||||||
State::Fading(start, _, hover) if start.elapsed() >= self.fade_dur => match hover {
|
State::Fading(start, _, hover) if start.elapsed() >= self.fade_dur => match hover {
|
||||||
Some(hover) => State::Start(hover),
|
Some(hover) => State::Start(hover),
|
||||||
None => State::Idle,
|
None => State::Idle,
|
||||||
},
|
},
|
||||||
state @ State::Idle | state @ State::Start(_) | state @ State::Showing(_) | state @ State::Fading(_, _, _) => state,
|
state @ State::Idle
|
||||||
}
|
| state @ State::Start(_)
|
||||||
|
| state @ State::Showing(_)
|
||||||
|
| state @ State::Fading(_, _, _) => state,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, update: Update) {
|
fn update(&self, update: Update) { *self.update.lock().unwrap() = Some(update); }
|
||||||
self.hover_widget = Some(update.0.0);
|
|
||||||
self.hover_pos = update.0.1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn showing(&self, aabr: Aabr<i32>) -> bool {
|
fn showing(&self, aabr: Aabr<i32>) -> bool {
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Idle | State::Start(_) => false,
|
State::Idle | State::Start(_) => false,
|
||||||
State::Showing(show) | State::Fading(_, show, _) => show.aabr == aabr,
|
State::Showing(show) | State::Fading(_, show, _) => show.aabr == aabr,
|
||||||
@ -108,12 +127,10 @@ impl TooltipManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A widget used to display tooltips when the content element is hovered
|
/// A widget used to display tooltips when the content element is hovered
|
||||||
pub struct Tooltip<'a, M, R: iced::Renderer> {
|
pub struct Tooltip<'a, M, R: iced::Renderer> {
|
||||||
content: Element<'a, M, R>,
|
content: Element<'a, M, R>,
|
||||||
hover_content: Option<Box<dyn 'a + FnOnce() -> Element<'a, M, R>>>,
|
hover_content: Box<dyn 'a + FnMut() -> Element<'a, M, R>>,
|
||||||
on_update: Box<dyn Fn(Update) -> M>,
|
|
||||||
manager: &'a TooltipManager,
|
manager: &'a TooltipManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,16 +138,14 @@ impl<'a, M, R> Tooltip<'a, M, R>
|
|||||||
where
|
where
|
||||||
R: iced::Renderer,
|
R: iced::Renderer,
|
||||||
{
|
{
|
||||||
pub fn new<C, H, F>(content: C, hover_content: H, on_update: F, manager: &'a TooltipManager) -> Self
|
pub fn new<C, H>(content: C, hover_content: H, manager: &'a TooltipManager) -> Self
|
||||||
where
|
where
|
||||||
C: Into<Element<'a, M, R>>,
|
C: Into<Element<'a, M, R>>,
|
||||||
H: 'a + FnOnce() -> Element<'a, M, R>,
|
H: 'a + FnMut() -> Element<'a, M, R>,
|
||||||
F: 'static + Fn(Update) -> M,
|
|
||||||
{
|
{
|
||||||
Self {
|
Self {
|
||||||
content: content.into(),
|
content: content.into(),
|
||||||
hover_content: Some(Box::new(hover_content)),
|
hover_content: Box::new(hover_content),
|
||||||
on_update: Box::new(on_update),
|
|
||||||
manager,
|
manager,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,13 +172,6 @@ where
|
|||||||
renderer: &R,
|
renderer: &R,
|
||||||
clipboard: Option<&dyn Clipboard>,
|
clipboard: Option<&dyn Clipboard>,
|
||||||
) {
|
) {
|
||||||
let bounds = layout.bounds();
|
|
||||||
if bounds.contains(cursor_position) {
|
|
||||||
let aabr = aabr_from_bounds(bounds);
|
|
||||||
let m_pos = Vec2::new(cursor_position.x.trunc() as i32, cursor_position.y.trunc() as i32);
|
|
||||||
messages.push((self.on_update)(Update((aabr, m_pos))));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.content.on_event(
|
self.content.on_event(
|
||||||
event,
|
event,
|
||||||
layout,
|
layout,
|
||||||
@ -181,32 +189,33 @@ where
|
|||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
) -> R::Output {
|
) -> R::Output {
|
||||||
self.content.draw(
|
let bounds = layout.bounds();
|
||||||
renderer,
|
if bounds.contains(cursor_position) {
|
||||||
defaults,
|
let aabr = aabr_from_bounds(bounds);
|
||||||
layout,
|
let m_pos = Vec2::new(
|
||||||
cursor_position,
|
cursor_position.x.trunc() as i32,
|
||||||
)
|
cursor_position.y.trunc() as i32,
|
||||||
|
);
|
||||||
|
self.manager.update(Update((aabr, m_pos)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn overlay(
|
self.content
|
||||||
&mut self,
|
.draw(renderer, defaults, layout, cursor_position)
|
||||||
layout: Layout<'_>,
|
}
|
||||||
) -> Option<iced::overlay::Element<'_, M, R>> {
|
|
||||||
|
fn overlay(&mut self, layout: Layout<'_>) -> Option<iced::overlay::Element<'_, M, R>> {
|
||||||
let bounds = layout.bounds();
|
let bounds = layout.bounds();
|
||||||
let aabr = aabr_from_bounds(bounds);
|
let aabr = aabr_from_bounds(bounds);
|
||||||
|
|
||||||
self.manager.showing(aabr)
|
self.manager.showing(aabr).then(|| {
|
||||||
.then(|| self.hover_content.take())
|
iced::overlay::Element::new(
|
||||||
.flatten()
|
Point {
|
||||||
.map(|content| iced::overlay::Element::new(
|
x: self.manager.hover_pos.x as f32,
|
||||||
Point { x: self.manager.hover_pos.x as f32, y: self.manager.hover_pos.y as f32 },
|
y: self.manager.hover_pos.y as f32,
|
||||||
Box::new(Overlay::new(
|
},
|
||||||
content(),
|
Box::new(Overlay::new((self.hover_content)(), bounds)),
|
||||||
bounds,
|
)
|
||||||
))
|
})
|
||||||
))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_layout(&self, state: &mut Hasher) {
|
fn hash_layout(&self, state: &mut Hasher) {
|
||||||
@ -236,24 +245,15 @@ struct Overlay<'a, M, R: iced::Renderer> {
|
|||||||
avoid: Rectangle,
|
avoid: Rectangle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, M, R: iced::Renderer> Overlay<'a, M, R>
|
impl<'a, M, R: iced::Renderer> Overlay<'a, M, R> {
|
||||||
{
|
pub fn new(content: Element<'a, M, R>, avoid: Rectangle) -> Self { Self { content, avoid } }
|
||||||
pub fn new(content: Element<'a, M, R>, avoid: Rectangle) -> Self {
|
|
||||||
Self { content, avoid }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, M, R> iced::Overlay<M, R>
|
impl<'a, M, R> iced::Overlay<M, R> for Overlay<'a, M, R>
|
||||||
for Overlay<'a, M, R>
|
|
||||||
where
|
where
|
||||||
R: iced::Renderer,
|
R: iced::Renderer,
|
||||||
{
|
{
|
||||||
fn layout(
|
fn layout(&self, renderer: &R, bounds: Size, position: Point) -> layout::Node {
|
||||||
&self,
|
|
||||||
renderer: &R,
|
|
||||||
bounds: Size,
|
|
||||||
position: Point,
|
|
||||||
) -> layout::Node {
|
|
||||||
// TODO: Avoid avoid area
|
// TODO: Avoid avoid area
|
||||||
let space_below = bounds.height - position.y;
|
let space_below = bounds.height - position.y;
|
||||||
let space_above = position.y;
|
let space_above = position.y;
|
||||||
@ -274,8 +274,6 @@ where
|
|||||||
let mut node = self.content.layout(renderer, &limits);
|
let mut node = self.content.layout(renderer, &limits);
|
||||||
|
|
||||||
node.move_to(position - iced::Vector::new(0.0, node.size().height));
|
node.move_to(position - iced::Vector::new(0.0, node.size().height));
|
||||||
|
|
||||||
node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hash_layout(&self, state: &mut Hasher, position: Point) {
|
fn hash_layout(&self, state: &mut Hasher, position: Point) {
|
||||||
@ -294,6 +292,7 @@ where
|
|||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor_position: Point,
|
cursor_position: Point,
|
||||||
) -> R::Output {
|
) -> R::Output {
|
||||||
self.content.draw(renderer, defaults, layout, cursor_position)
|
self.content
|
||||||
|
.draw(renderer, defaults, layout, cursor_position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user