mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Fix tooltip border images, fade tooltips properly, improve tooltip positioning
This commit is contained in:
parent
d8854e9300
commit
3b664d2efb
BIN
assets/voxygen/element/frames/tooltip/corner.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/tooltip/corner.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/frames/tooltip/edge.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/frames/tooltip/edge.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -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<u8> = 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),
|
||||
)
|
||||
|
@ -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<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 {
|
||||
impl<'a, M, R: ui::widget::tooltip::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>,
|
||||
|
@ -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<u32>, renderer: &mut Renderer) {
|
||||
fn draw_primitive(
|
||||
&mut self,
|
||||
primitive: Primitive,
|
||||
offset: Vec2<u32>,
|
||||
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<f32>, alpha: f32) -> Rgba<f32> {
|
||||
Rgba {
|
||||
a: alpha * color.a,
|
||||
..color
|
||||
}
|
||||
}
|
||||
// TODO: impl Debugger
|
||||
|
@ -36,5 +36,10 @@ pub enum Primitive {
|
||||
offset: vek::Vec2<u32>,
|
||||
content: Box<Primitive>,
|
||||
},
|
||||
// Make content translucent
|
||||
Opacity {
|
||||
alpha: f32,
|
||||
content: Box<Primitive>,
|
||||
},
|
||||
Nothing,
|
||||
}
|
||||
|
@ -13,3 +13,4 @@ mod slider;
|
||||
mod space;
|
||||
mod text;
|
||||
mod text_input;
|
||||
mod tooltip;
|
||||
|
23
voxygen/src/ui/ice/renderer/widget/tooltip.rs
Normal file
23
voxygen/src/ui/ice/renderer/widget/tooltip.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use super::super::{super::widget::tooltip, IcedRenderer, Primitive};
|
||||
use iced::{Element, Layout, Point};
|
||||
|
||||
impl tooltip::Renderer for IcedRenderer {
|
||||
fn draw<M>(
|
||||
&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,
|
||||
)
|
||||
}
|
||||
}
|
@ -119,16 +119,37 @@ impl TooltipManager {
|
||||
|
||||
fn update(&self, update: Update) { *self.update.lock().unwrap() = Some(update); }
|
||||
|
||||
fn showing(&self, aabr: Aabr<i32>) -> 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<i32>) -> 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<dyn 'a + FnMut() -> 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<C, H>(content: C, hover_content: H, manager: &'a TooltipManager) -> Self
|
||||
where
|
||||
@ -153,7 +174,7 @@ where
|
||||
|
||||
impl<'a, M, R> Widget<M, R> 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<Tooltip<'a, M, R>> 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<i32> {
|
||||
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<M, R> 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<M>(
|
||||
&mut self,
|
||||
alpha: f32,
|
||||
defaults: &Self::Defaults,
|
||||
cursor_position: Point,
|
||||
content: &Element<'_, M, Self>,
|
||||
content_layout: Layout<'_>,
|
||||
) -> Self::Output;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user