From 61a0aa2055f55782de05b00c0161fcbf618f8040 Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 18 Oct 2020 17:30:17 -0400 Subject: [PATCH] Fix tooltip border images, fade tooltips properly, improve tooltip positioning --- .../voxygen/element/frames/tooltip/corner.png | Bin 0 -> 6484 bytes .../voxygen/element/frames/tooltip/edge.png | Bin 0 -> 5966 bytes voxygen/src/menu/char_selection/ui/mod.rs | 39 ++++--- voxygen/src/ui/ice/component/tooltip.rs | 8 +- voxygen/src/ui/ice/renderer/mod.rs | 34 ++++-- voxygen/src/ui/ice/renderer/primitive.rs | 5 + voxygen/src/ui/ice/renderer/widget/mod.rs | 1 + voxygen/src/ui/ice/renderer/widget/tooltip.rs | 23 ++++ voxygen/src/ui/ice/widget/tooltip.rs | 109 ++++++++++++------ 9 files changed, 158 insertions(+), 61 deletions(-) create mode 100644 assets/voxygen/element/frames/tooltip/corner.png create mode 100644 assets/voxygen/element/frames/tooltip/edge.png create mode 100644 voxygen/src/ui/ice/renderer/widget/tooltip.rs diff --git a/assets/voxygen/element/frames/tooltip/corner.png b/assets/voxygen/element/frames/tooltip/corner.png new file mode 100644 index 0000000000000000000000000000000000000000..fff6903304fc6d10e42ec353347cd25e3143dd04 GIT binary patch literal 6484 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 a9T}y$CIN$kKT4ua7~+6X|8n2BtUm#q{+|y3 literal 0 HcmV?d00001 diff --git a/voxygen/src/menu/char_selection/ui/mod.rs b/voxygen/src/menu/char_selection/ui/mod.rs index 2b0246efa9..d1fd907d7c 100644 --- a/voxygen/src/menu/char_selection/ui/mod.rs +++ b/voxygen/src/menu/char_selection/ui/mod.rs @@ -42,8 +42,8 @@ pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1. pub const TOOLTIP_BACK_COLOR: Rgba = Rgba::new(20, 18, 10, 255); const FILL_FRAC_ONE: f32 = 0.77; 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 TOOLTIP_HOVER_DUR: std::time::Duration = std::time::Duration::from_millis(300); +const TOOLTIP_FADE_DUR: std::time::Duration = std::time::Duration::from_millis(400); const STARTER_HAMMER: &str = "common.items.weapons.hammer.starter_hammer"; const STARTER_BOW: &str = "common.items.weapons.bow.starter_bow"; @@ -112,8 +112,8 @@ image_ids_ice! { button_press: "voxygen.element.buttons.button_press", // Tooltips - tt_edge: "voxygen/element/frames/tt_test_edge", - tt_corner: "voxygen/element/frames/tt_test_corner_tr", + tt_edge: "voxygen/element/frames/tooltip/edge", + tt_corner: "voxygen/element/frames/tooltip/corner", } } @@ -304,7 +304,8 @@ impl Controls { imgs.tt_edge, ), text_color: TEXT_COLOR, - text_size: self.fonts.cyri.scale(15), + text_size: self.fonts.cyri.scale(17), + padding: 10, }; let version = iced::Text::new(&self.version) @@ -384,7 +385,7 @@ impl Controls { .map(|(i, (character, (select_button, delete_button)))| { Overlay::new( Button::new( - select_button, + delete_button, Space::new(Length::Units(16), Length::Units(16)), ) .style( @@ -392,10 +393,19 @@ impl Controls { .hover_image(imgs.delete_button_hover) .press_image(imgs.delete_button_press), ) - .on_press(Message::Delete(i)), + .on_press(Message::Delete(i)) + .with_tooltip( + tooltip_manager, + move || { + tooltip::text( + i18n.get("char_selection.delete_permanently"), + tooltip_style, + ) + }, + ), AspectRatioContainer::new( Button::new( - delete_button, + select_button, Column::with_children(vec![ Text::new(&character.character.alias).into(), // TODO: only construct string once when characters are @@ -411,7 +421,7 @@ impl Controls { .into(), ]), ) - .padding(8) + .padding(10) .style( style::button::Style::new(imgs.selection) .hover_image(imgs.selection_hover) @@ -419,16 +429,7 @@ impl Controls { ) .width(Length::Fill) .height(Length::Fill) - .on_press(Message::Select(i)) - .with_tooltip( - tooltip_manager, - move || { - tooltip::text( - i18n.get("char_selection.delete_permanently"), - tooltip_style, - ) - }, - ), + .on_press(Message::Select(i)), ) .ratio_of_image(imgs.selection), ) diff --git a/voxygen/src/ui/ice/component/tooltip.rs b/voxygen/src/ui/ice/component/tooltip.rs index be27c83959..c53c693095 100644 --- a/voxygen/src/ui/ice/component/tooltip.rs +++ b/voxygen/src/ui/ice/component/tooltip.rs @@ -11,6 +11,7 @@ pub struct Style { pub container: style::container::Style, pub text_color: iced::Color, pub text_size: u16, + pub padding: u16, } /// Tooltip that is just text @@ -21,16 +22,19 @@ pub fn text<'a, M: 'a>(text: &'a str, style: Style) -> Element<'a, M, ui::IcedRe .size(style.text_size), ) .style(style.container) + .padding(style.padding) .into() } -pub trait WithTooltip<'a, M, R: iced::Renderer> { +pub trait WithTooltip<'a, M, R: ui::widget::tooltip::Renderer> { fn with_tooltip(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>> WithTooltip<'a, M, R> for E { +impl<'a, M, R: ui::widget::tooltip::Renderer, E: Into>> WithTooltip<'a, M, R> + for E +{ fn with_tooltip(self, manager: &'a TooltipManager, hover_content: H) -> Tooltip<'a, M, R> where H: 'a + FnMut() -> Element<'a, M, R>, diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index b1f200aa45..c507b4c42f 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -181,7 +181,7 @@ impl IcedRenderer { //self.current_scissor = default_scissor(renderer); - self.draw_primitive(primitive, Vec2::zero(), renderer); + self.draw_primitive(primitive, Vec2::zero(), 1.0, renderer); // Enter the final command. self.draw_commands.push(match self.current_state { @@ -434,12 +434,18 @@ impl IcedRenderer { .collect() } - fn draw_primitive(&mut self, primitive: Primitive, offset: Vec2, renderer: &mut Renderer) { + fn draw_primitive( + &mut self, + primitive: Primitive, + offset: Vec2, + alpha: f32, + renderer: &mut Renderer, + ) { match primitive { Primitive::Group { primitives } => { primitives .into_iter() - .for_each(|p| self.draw_primitive(p, offset, renderer)); + .for_each(|p| self.draw_primitive(p, offset, alpha, renderer)); }, Primitive::Image { handle, @@ -447,8 +453,9 @@ impl IcedRenderer { color, } => { let color = srgba_to_linear(color.map(|e| e as f32 / 255.0)); + let color = apply_alpha(color, alpha); // Don't draw a transparent image. - if color[3] == 0.0 { + if color.a == 0.0 { return; } @@ -524,7 +531,9 @@ impl IcedRenderer { bottom_linear_color, } => { // Don't draw a transparent rectangle. - if top_linear_color[3] == 0.0 && bottom_linear_color[3] == 0.0 { + let top_linear_color = apply_alpha(top_linear_color, alpha); + let bottom_linear_color = apply_alpha(bottom_linear_color, alpha); + if top_linear_color.a == 0.0 && bottom_linear_color.a == 0.0 { return; } @@ -552,8 +561,9 @@ impl IcedRenderer { bounds, linear_color, } => { + let linear_color = apply_alpha(linear_color, alpha); // Don't draw a transparent rectangle. - if linear_color[3] == 0.0 { + if linear_color.a == 0.0 { return; } @@ -583,6 +593,7 @@ impl IcedRenderer { *horizontal_alignment, *vertical_alignment, */ } => { + let linear_color = apply_alpha(linear_color, alpha); self.switch_state(State::Plain); // TODO: makes sure we are not doing all this work for hidden text @@ -684,7 +695,7 @@ impl IcedRenderer { // TODO: cull primitives outside the current scissor // Renderer child - self.draw_primitive(*content, offset + clip_offset, renderer); + self.draw_primitive(*content, offset + clip_offset, alpha, renderer); // Reset scissor self.draw_commands.push(match self.current_state { @@ -698,6 +709,9 @@ impl IcedRenderer { self.draw_commands .push(DrawCommand::Scissor(self.window_scissor)); }, + Primitive::Opacity { alpha: a, content } => { + self.draw_primitive(*content, offset, alpha * a, renderer); + }, Primitive::Nothing => {}, } } @@ -806,4 +820,10 @@ impl iced::Renderer for IcedRenderer { } } +fn apply_alpha(color: Rgba, alpha: f32) -> Rgba { + Rgba { + a: alpha * color.a, + ..color + } +} // TODO: impl Debugger diff --git a/voxygen/src/ui/ice/renderer/primitive.rs b/voxygen/src/ui/ice/renderer/primitive.rs index 2ce358bc27..8230287130 100644 --- a/voxygen/src/ui/ice/renderer/primitive.rs +++ b/voxygen/src/ui/ice/renderer/primitive.rs @@ -36,5 +36,10 @@ pub enum Primitive { offset: vek::Vec2, content: Box, }, + // Make content translucent + Opacity { + alpha: f32, + content: Box, + }, Nothing, } diff --git a/voxygen/src/ui/ice/renderer/widget/mod.rs b/voxygen/src/ui/ice/renderer/widget/mod.rs index d165b6b2a8..8f9c84758e 100644 --- a/voxygen/src/ui/ice/renderer/widget/mod.rs +++ b/voxygen/src/ui/ice/renderer/widget/mod.rs @@ -13,3 +13,4 @@ mod slider; mod space; mod text; mod text_input; +mod tooltip; diff --git a/voxygen/src/ui/ice/renderer/widget/tooltip.rs b/voxygen/src/ui/ice/renderer/widget/tooltip.rs new file mode 100644 index 0000000000..626096dd0e --- /dev/null +++ b/voxygen/src/ui/ice/renderer/widget/tooltip.rs @@ -0,0 +1,23 @@ +use super::super::{super::widget::tooltip, IcedRenderer, Primitive}; +use iced::{Element, Layout, Point}; + +impl tooltip::Renderer for IcedRenderer { + fn draw( + &mut self, + alpha: f32, + defaults: &Self::Defaults, + cursor_position: Point, + content: &Element<'_, M, Self>, + content_layout: Layout<'_>, + ) -> Self::Output { + let (primitive, cursor_interaction) = + content.draw(self, defaults, content_layout, cursor_position); + ( + Primitive::Opacity { + alpha, + content: Box::new(primitive), + }, + cursor_interaction, + ) + } +} diff --git a/voxygen/src/ui/ice/widget/tooltip.rs b/voxygen/src/ui/ice/widget/tooltip.rs index e686b2b4a1..0a7d496b2c 100644 --- a/voxygen/src/ui/ice/widget/tooltip.rs +++ b/voxygen/src/ui/ice/widget/tooltip.rs @@ -119,16 +119,37 @@ impl TooltipManager { fn update(&self, update: Update) { *self.update.lock().unwrap() = Some(update); } - fn showing(&self, aabr: Aabr) -> bool { + /// Returns an options with the position of the cursor when the tooltip + /// started being show and the transparency if it is fading + fn showing(&self, aabr: Aabr) -> Option<(Point, f32)> { match self.state { - State::Idle | State::Start(_) => false, - State::Showing(show) | State::Fading(_, show, _) => show.aabr == aabr, + State::Idle | State::Start(_) => None, + State::Showing(show) => (show.aabr == aabr).then(|| { + ( + Point { + x: show.hover_pos.x as f32, + y: show.hover_pos.y as f32, + }, + 1.0, + ) + }), + State::Fading(start, show, _) => (show.aabr == aabr) + .then(|| { + ( + Point { + x: show.hover_pos.x as f32, + y: show.hover_pos.y as f32, + }, + 1.0 - start.elapsed().as_secs_f32() / self.fade_dur.as_secs_f32(), + ) + }) + .filter(|(_, fade)| *fade > 0.0), } } } /// 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: self::Renderer> { content: Element<'a, M, R>, hover_content: Box Element<'a, M, R>>, manager: &'a TooltipManager, @@ -136,7 +157,7 @@ pub struct Tooltip<'a, M, R: iced::Renderer> { impl<'a, M, R> Tooltip<'a, M, R> where - R: iced::Renderer, + R: self::Renderer, { pub fn new(content: C, hover_content: H, manager: &'a TooltipManager) -> Self where @@ -153,7 +174,7 @@ where impl<'a, M, R> Widget for Tooltip<'a, M, R> where - R: iced::Renderer, + R: self::Renderer, { fn width(&self) -> Length { self.content.width() } @@ -207,13 +228,10 @@ where let bounds = layout.bounds(); let aabr = aabr_from_bounds(bounds); - self.manager.showing(aabr).then(|| { + self.manager.showing(aabr).map(|(pos, alpha)| { iced::overlay::Element::new( - Point { - x: self.manager.hover_pos.x as f32, - y: self.manager.hover_pos.y as f32, - }, - Box::new(Overlay::new((self.hover_content)(), bounds)), + pos, + Box::new(Overlay::new((self.hover_content)(), bounds, alpha)), ) }) } @@ -227,7 +245,7 @@ where impl<'a, M, R> From> for Element<'a, M, R> where - R: 'a + iced::Renderer, + R: 'a + self::Renderer, M: 'a, { fn from(tooltip: Tooltip<'a, M, R>) -> Element<'a, M, R> { Element::new(tooltip) } @@ -239,41 +257,56 @@ fn aabr_from_bounds(bounds: iced::Rectangle) -> Aabr { Aabr { min, max } } -struct Overlay<'a, M, R: iced::Renderer> { +struct Overlay<'a, M, R: self::Renderer> { content: Element<'a, M, R>, /// Area to avoid overlapping with avoid: Rectangle, + /// Alpha for fading out + alpha: f32, } -impl<'a, M, R: iced::Renderer> Overlay<'a, M, R> { - pub fn new(content: Element<'a, M, R>, avoid: Rectangle) -> Self { Self { content, avoid } } +impl<'a, M, R: self::Renderer> Overlay<'a, M, R> { + pub fn new(content: Element<'a, M, R>, avoid: Rectangle, alpha: f32) -> Self { + Self { + content, + avoid, + alpha, + } + } } impl<'a, M, R> iced::Overlay for Overlay<'a, M, R> where - R: iced::Renderer, + R: self::Renderer, { fn layout(&self, renderer: &R, bounds: Size, position: Point) -> layout::Node { - // TODO: Avoid avoid area - let space_below = bounds.height - position.y; - let space_above = position.y; + let avoid = self.avoid; + const PAD: f32 = 8.0; // TODO: allow configuration + let space_above = (avoid.y - PAD).max(0.0); + let space_below = (bounds.height - avoid.y - avoid.height - PAD).max(0.0); + //let space_left = avoid.x.min(0.0) + //let space_right = (bounds.width - avoid.x - avoid.width).min(0.0); let limits = layout::Limits::new( Size::ZERO, - Size::new( - bounds.width - position.x, - if space_below > space_above { - space_below - } else { - space_above - }, - ), - ) - .width(self.content.width()); + Size::new(bounds.width, space_above.max(space_below)), + ); + //.width(self.content.width()); let mut node = self.content.layout(renderer, &limits); - node.move_to(position - iced::Vector::new(0.0, node.size().height)); + let size = node.size(); + + node.move_to(Point { + x: (bounds.width - size.width).min(position.x), + y: if space_above >= space_below { + avoid.y - size.height - PAD + } else { + avoid.y + avoid.height + PAD + }, + }); + + node } fn hash_layout(&self, state: &mut Hasher, position: Point) { @@ -292,7 +325,17 @@ where layout: Layout<'_>, cursor_position: Point, ) -> R::Output { - self.content - .draw(renderer, defaults, layout, cursor_position) + renderer.draw(self.alpha, defaults, cursor_position, &self.content, layout) } } + +pub trait Renderer: iced::Renderer { + fn draw( + &mut self, + alpha: f32, + defaults: &Self::Defaults, + cursor_position: Point, + content: &Element<'_, M, Self>, + content_layout: Layout<'_>, + ) -> Self::Output; +}