mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Convert tooltips to use ImageFrame, add autosizing
This commit is contained in:
parent
e9bedf529d
commit
3ccbb0f38d
@ -56,7 +56,7 @@ pub fn rgb_to_hsv(rgb: Rgb<f32>) -> Vec3<f32> {
|
||||
let h = if max == min {
|
||||
0.0
|
||||
} else {
|
||||
let mut h = (60.0 * (add + diff / (max - min)));
|
||||
let mut h = 60.0 * (add + diff / (max - min));
|
||||
if h < 0.0 {
|
||||
h += 360.0;
|
||||
}
|
||||
|
@ -58,11 +58,6 @@ widget_ids! {
|
||||
error_frame,
|
||||
button_ok,
|
||||
version,
|
||||
|
||||
|
||||
// TODO remove
|
||||
frame_test,
|
||||
test2,
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,8 +85,8 @@ rotation_image_ids! {
|
||||
<VoxelGraphic>
|
||||
|
||||
// Tooltip Test
|
||||
tt_side: "voxygen/element/frames/tt_test_edge.vox",
|
||||
tt_corner: "voxygen/element/frames/tt_test_corner_tr.vox",
|
||||
tt_side: "voxygen/element/frames/tt_test_edge",
|
||||
tt_corner: "voxygen/element/frames/tt_test_corner_tr",
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,26 +171,6 @@ impl MainMenuUi {
|
||||
.top_right_with_margins(30.0, 30.0)
|
||||
.set(self.ids.v_logo, ui_widgets);
|
||||
|
||||
Image::new(self.rot_imgs.tt_side.cw90)
|
||||
.w_h(50.0, 50.0)
|
||||
.top_left_with_margins_on(ui_widgets.window, 25.0, 25.0)
|
||||
.set(self.ids.test2, ui_widgets);
|
||||
// TODO: remove me
|
||||
// Test image frame
|
||||
// Edge images [t, b, r, l]
|
||||
// Corner images [tr, tl, br, bl]
|
||||
let edge = &self.rot_imgs.tt_side;
|
||||
let corner = &self.rot_imgs.tt_corner;
|
||||
ImageFrame::new(
|
||||
[edge.cw180, edge.none, edge.cw270, edge.cw90],
|
||||
[corner.none, corner.cw270, corner.cw90, corner.cw180],
|
||||
Color::Rgba(0.8, 0.7, 0.4, 1.0),
|
||||
30.0,
|
||||
)
|
||||
.w_h(250.0, 200.0)
|
||||
.down(5.0)
|
||||
.set(self.ids.frame_test, ui_widgets);
|
||||
|
||||
Text::new(version)
|
||||
.top_right_with_margins_on(ui_widgets.window, 5.0, 5.0)
|
||||
.font_size(14)
|
||||
@ -505,11 +480,23 @@ impl MainMenuUi {
|
||||
.label_y(Relative::Scalar(5.0))
|
||||
.with_tooltip(
|
||||
tooltip_manager,
|
||||
Tooltip::new("Login", "Click to login with the entered details")
|
||||
.title_font_size(15)
|
||||
.desc_font_size(10)
|
||||
.title_text_color(TEXT_COLOR)
|
||||
.desc_text_color(TEXT_COLOR_2),
|
||||
// TODO improve this interface
|
||||
Tooltip::new("Login", "Click to login with the entered details", &{
|
||||
// Edge images [t, b, r, l]
|
||||
// Corner images [tr, tl, br, bl]
|
||||
let edge = &self.rot_imgs.tt_side;
|
||||
let corner = &self.rot_imgs.tt_corner;
|
||||
ImageFrame::new(
|
||||
[edge.cw180, edge.none, edge.cw270, edge.cw90],
|
||||
[corner.none, corner.cw270, corner.cw90, corner.cw180],
|
||||
Color::Rgba(0.08, 0.07, 0.04, 1.0),
|
||||
5.0,
|
||||
)
|
||||
})
|
||||
.title_font_size(60)
|
||||
.desc_font_size(10)
|
||||
.title_text_color(TEXT_COLOR)
|
||||
.desc_text_color(TEXT_COLOR_2),
|
||||
)
|
||||
.set(self.ids.login_button, ui_widgets)
|
||||
.was_clicked()
|
||||
|
@ -12,7 +12,7 @@ use conrod_core::{
|
||||
///
|
||||
/// Its reaction is triggered if the value is updated or if the mouse button is released while
|
||||
/// the cursor is above the rectangle.
|
||||
#[derive(WidgetCommon)]
|
||||
#[derive(Clone, WidgetCommon)]
|
||||
pub struct ImageFrame {
|
||||
#[conrod(common_builder)]
|
||||
common: widget::CommonBuilder,
|
||||
@ -32,6 +32,7 @@ pub struct ImageFrame {
|
||||
// TODO: would it be useful to have an optional close button be a part of this?
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Center {
|
||||
Plain(Color),
|
||||
Image(image::Id, Option<Rect>),
|
||||
@ -52,6 +53,7 @@ impl From<(image::Id, Rect)> for Center {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BorderSize {
|
||||
top: f64,
|
||||
bottom: f64,
|
||||
@ -296,7 +298,12 @@ impl Widget for ImageFrame {
|
||||
.xy(rect.xy())
|
||||
.parent(id)
|
||||
.graphics_for(id)
|
||||
.and_then(maybe_color, |w, c| w.color(c))
|
||||
.and_then(maybe_color, |w, c| {
|
||||
w.color(color.alpha(match c {
|
||||
Color::Rgba(_, _, _, a) => a,
|
||||
Color::Hsla(_, _, _, a) => a,
|
||||
}))
|
||||
})
|
||||
.set(state.ids.center_plain, ui);
|
||||
}
|
||||
Center::Image(image_id, maybe_src_rect) => {
|
||||
|
@ -1,6 +1,8 @@
|
||||
use super::image_frame::ImageFrame;
|
||||
use conrod_core::{
|
||||
builder_method, builder_methods, input::global::Global, text, widget, widget_ids, Color,
|
||||
Colorable, FontSize, Positionable, Sizeable, UiCell, Widget, WidgetCommon, WidgetStyle,
|
||||
builder_method, builder_methods, image, input::global::Global, position::Dimension, text,
|
||||
widget, widget_ids, Color, Colorable, FontSize, Positionable, Sizeable, Ui, UiCell, Widget,
|
||||
WidgetCommon, WidgetStyle,
|
||||
};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
@ -162,6 +164,15 @@ impl<W: Widget> Tooltipable for W {
|
||||
}
|
||||
}
|
||||
|
||||
/// Vertical spacing between elements of the tooltip
|
||||
const V_PAD: f64 = 10.0;
|
||||
/// Horizontal spacing between elements of the tooltip
|
||||
const H_PAD: f64 = 10.0;
|
||||
/// Default portion of inner width that goes to an image
|
||||
const IMAGE_W_FRAC: f64 = 0.3;
|
||||
/// Default width multiplied by the description font size
|
||||
const DEFAULT_CHAR_W: f64 = 30.0;
|
||||
|
||||
/// A widget for displaying tooltips
|
||||
#[derive(Clone, WidgetCommon)]
|
||||
pub struct Tooltip<'a> {
|
||||
@ -169,13 +180,16 @@ pub struct Tooltip<'a> {
|
||||
common: widget::CommonBuilder,
|
||||
title_text: &'a str,
|
||||
desc_text: &'a str,
|
||||
image: Option<image::Id>,
|
||||
image_dims: Option<(f64, f64)>,
|
||||
style: Style,
|
||||
transparency: f32,
|
||||
image_frame: &'a ImageFrame,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, WidgetStyle)]
|
||||
pub struct Style {
|
||||
#[conrod(default = "theme.background_color")]
|
||||
#[conrod(default = "Color::Rgba(1.0, 1.0, 1.0, 1.0)")]
|
||||
pub color: Option<Color>,
|
||||
title: widget::text::Style,
|
||||
desc: widget::text::Style,
|
||||
@ -186,7 +200,8 @@ widget_ids! {
|
||||
struct Ids {
|
||||
title,
|
||||
desc,
|
||||
back_rect,
|
||||
image_frame,
|
||||
image,
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,13 +210,16 @@ pub struct State {
|
||||
}
|
||||
|
||||
impl<'a> Tooltip<'a> {
|
||||
pub fn new(title: &'a str, desc: &'a str) -> Self {
|
||||
pub fn new(title: &'a str, desc: &'a str, image_frame: &'a ImageFrame) -> Self {
|
||||
Tooltip {
|
||||
common: widget::CommonBuilder::default(),
|
||||
style: Style::default(),
|
||||
title_text: title,
|
||||
desc_text: desc,
|
||||
transparency: 1.0,
|
||||
image_frame,
|
||||
image: None,
|
||||
image_dims: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +238,29 @@ impl<'a> Tooltip<'a> {
|
||||
// self.justify(text::Justify::Right)
|
||||
//}
|
||||
|
||||
// TODO: add method(s) to make children widgets and use that to determine height in height function (and in update to draw the widgets)
|
||||
fn title_desc_image_width(&self, total_width: f64) -> (f64, f64, f64) {
|
||||
let inner_width = (total_width - H_PAD * 2.0).max(0.0);
|
||||
let title_w = inner_width;
|
||||
// Image defaults to 30% of the width
|
||||
let image_w = if self.image.is_some() {
|
||||
match self.image_dims {
|
||||
Some((w, _)) => w,
|
||||
None => (inner_width - H_PAD).max(0.0) * IMAGE_W_FRAC,
|
||||
}
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
// Description gets the remaining width
|
||||
let desc_w = (inner_width
|
||||
- if self.image.is_some() {
|
||||
image_w + H_PAD
|
||||
} else {
|
||||
0.0
|
||||
})
|
||||
.max(0.0);
|
||||
|
||||
(title_w, desc_w, image_w)
|
||||
}
|
||||
|
||||
/// Specify the font used for displaying the text.
|
||||
pub fn font_id(mut self, font_id: text::font::Id) -> Self {
|
||||
@ -236,6 +276,8 @@ impl<'a> Tooltip<'a> {
|
||||
pub desc_font_size { style.desc.font_size = Some(FontSize) }
|
||||
pub title_justify { style.title.justify = Some(text::Justify) }
|
||||
pub desc_justify { style.desc.justify = Some(text::Justify) }
|
||||
pub image { image = Some(image::Id) }
|
||||
pub image_dims { image_dims = Some((f64, f64)) }
|
||||
transparency { transparency = f32 }
|
||||
}
|
||||
}
|
||||
@ -268,35 +310,105 @@ impl<'a> Widget for Tooltip<'a> {
|
||||
// Apply transparency
|
||||
let color = style.color(ui.theme()).alpha(self.transparency);
|
||||
|
||||
// Background rectangle
|
||||
widget::Rectangle::fill(rect.dim())
|
||||
// Background image frame
|
||||
self.image_frame
|
||||
.clone()
|
||||
.wh(rect.dim())
|
||||
.xy(rect.xy())
|
||||
.graphics_for(id)
|
||||
.parent(id)
|
||||
.color(color)
|
||||
.set(state.ids.back_rect, ui);
|
||||
.set(state.ids.image_frame, ui);
|
||||
|
||||
// Widths
|
||||
let (title_w, desc_w, image_w) = self.title_desc_image_width(rect.w());
|
||||
|
||||
// Title of tooltip
|
||||
widget::Text::new(self.title_text)
|
||||
.w(rect.w())
|
||||
.w(title_w)
|
||||
.graphics_for(id)
|
||||
.parent(id)
|
||||
.top_left_with_margins_on(state.ids.back_rect, 5.0, 5.0)
|
||||
.top_left_with_margins_on(state.ids.image_frame, V_PAD, H_PAD)
|
||||
.with_style(self.style.title)
|
||||
// Apply transparency
|
||||
.color(style.title.color(ui.theme()).alpha(self.transparency))
|
||||
.set(state.ids.title, ui);
|
||||
|
||||
// Image
|
||||
if let Some(img_id) = self.image {
|
||||
widget::Image::new(img_id)
|
||||
.w_h(image_w, self.image_dims.map_or(image_w, |(_, h)| h))
|
||||
.graphics_for(id)
|
||||
.parent(id)
|
||||
.down_from(state.ids.title, V_PAD)
|
||||
.align_left_of(state.ids.title)
|
||||
.color(Some(color))
|
||||
.set(state.ids.image, ui);
|
||||
}
|
||||
|
||||
// Description of tooltip
|
||||
widget::Text::new(self.desc_text)
|
||||
.w(rect.w())
|
||||
let desc = widget::Text::new(self.desc_text)
|
||||
.w(desc_w)
|
||||
.graphics_for(id)
|
||||
.parent(id)
|
||||
.down_from(state.ids.title, 10.0)
|
||||
.with_style(self.style.desc)
|
||||
// Apply transparency
|
||||
.color(style.desc.color(ui.theme()).alpha(self.transparency))
|
||||
.set(state.ids.desc, ui);
|
||||
.with_style(self.style.desc);
|
||||
|
||||
if self.image.is_some() {
|
||||
desc.right_from(state.ids.image, H_PAD)
|
||||
.align_top_of(state.ids.image)
|
||||
} else {
|
||||
desc.down_from(state.ids.title, V_PAD)
|
||||
.align_left_of(state.ids.title)
|
||||
}
|
||||
.set(state.ids.desc, ui);
|
||||
}
|
||||
|
||||
/// Default width is based on the description font size unless the text is small enough to fit on a single line
|
||||
fn default_x_dimension(&self, ui: &Ui) -> Dimension {
|
||||
let single_line_title_w = widget::Text::new(self.title_text)
|
||||
.with_style(self.style.title)
|
||||
.get_w(ui)
|
||||
.unwrap_or(0.0);
|
||||
let single_line_desc_w = widget::Text::new(self.desc_text)
|
||||
.with_style(self.style.desc)
|
||||
.get_w(ui)
|
||||
.unwrap_or(0.0);
|
||||
let body_w = if self.image.is_some() {
|
||||
match self.image_dims {
|
||||
Some((w, _)) => w + single_line_desc_w + H_PAD,
|
||||
None => single_line_desc_w / (1.0 - IMAGE_W_FRAC) + H_PAD,
|
||||
}
|
||||
} else {
|
||||
single_line_desc_w
|
||||
};
|
||||
|
||||
let width = single_line_title_w
|
||||
.max(body_w)
|
||||
.min(self.style.desc.font_size(&ui.theme) as f64 * DEFAULT_CHAR_W)
|
||||
+ 2.0 * H_PAD;
|
||||
Dimension::Absolute(width)
|
||||
}
|
||||
|
||||
fn default_y_dimension(&self, ui: &Ui) -> Dimension {
|
||||
let (title_w, desc_w, image_w) = self.title_desc_image_width(self.get_w(ui).unwrap_or(0.0));
|
||||
let title_h = widget::Text::new(self.title_text)
|
||||
.with_style(self.style.title)
|
||||
.w(title_w)
|
||||
.get_h(ui)
|
||||
.unwrap_or(0.0);
|
||||
let desc_h = widget::Text::new(self.desc_text)
|
||||
.with_style(self.style.desc)
|
||||
.w(desc_w)
|
||||
.get_h(ui)
|
||||
.unwrap_or(0.0);
|
||||
// Image defaults to square shape
|
||||
let body_h = desc_h.max(self.image_dims.map_or(image_w, |(_, h)| h));
|
||||
// Title height + body height + padding/spacing
|
||||
// TODO: add spacing dependent on text size
|
||||
let height = title_h + body_h + 3.0 * V_PAD;
|
||||
Dimension::Absolute(height)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user