diff --git a/voxygen/src/ui/mod.rs b/voxygen/src/ui/mod.rs index d61c2c5006..f3df2953cc 100644 --- a/voxygen/src/ui/mod.rs +++ b/voxygen/src/ui/mod.rs @@ -12,6 +12,7 @@ pub use event::Event; pub use graphic::Graphic; pub use scale::{Scale, ScaleMode}; pub use widgets::{ + image_frame::ImageFrame, image_slider::ImageSlider, ingame::{Ingame, IngameAnchor, Ingameable}, toggle_button::ToggleButton, diff --git a/voxygen/src/ui/widgets/image_frame.rs b/voxygen/src/ui/widgets/image_frame.rs new file mode 100644 index 0000000000..18aa1e6709 --- /dev/null +++ b/voxygen/src/ui/widgets/image_frame.rs @@ -0,0 +1,321 @@ +//! A widget for selecting a single value along some linear range. +use conrod_core::{ + builder_methods, image, + position::Range, + utils, + widget::{self, Image, Rectangle}, + widget_ids, Color, Colorable, Positionable, Rect, Sizeable, UiCell, Widget, WidgetCommon, +}; + +/// Linear value selection. +/// +/// If the slider's width is greater than its height, it will automatically become a horizontal +/// slider, otherwise it will be a vertical slider. +/// +/// 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)] +pub struct ImageFrame { + #[conrod(common_builder)] + common: widget::CommonBuilder, + // TODO: use type that is not just an array + // Edge images [t, b, r, l] + edges: [image::Id; 4], + edge_src_rects: [Option; 4], + // Corner images [tr, tl, br, bl] + corners: [image::Id; 4], + corner_src_rects: [Option; 4], + // Center + center: Center, + // Thickness of the frame border, determines the size used for the edge and corner images + border_size: BorderSize, + // Color to apply to all images making up the image frame + color: Option, + // TODO: would it be useful to have an optional close button be a part of this? +} + +enum Center { + Plain(Color), + Image(image::Id, Option), +} +impl From for Center { + fn from(color: Color) -> Self { + Center::Plain(color) + } +} +impl From for Center { + fn from(image: image::Id) -> Self { + Center::Image(image, None) + } +} +impl From<(image::Id, Rect)> for Center { + fn from((image, src_rect): (image::Id, Rect)) -> Self { + Center::Image(image, Some(src_rect)) + } +} + +struct BorderSize { + top: f64, + bottom: f64, + right: f64, + left: f64, +} +impl From for BorderSize { + fn from(width: f64) -> Self { + BorderSize { + top: width, + bottom: width, + right: width, + left: width, + } + } +} +impl From<[f64; 2]> for BorderSize { + fn from([vertical, horizontal]: [f64; 2]) -> Self { + BorderSize { + top: horizontal, + bottom: horizontal, + right: vertical, + left: vertical, + } + } +} +impl From<[f64; 4]> for BorderSize { + fn from(vals: [f64; 4]) -> Self { + BorderSize { + top: vals[0], + bottom: vals[1], + right: vals[2], + left: vals[3], + } + } +} + +widget_ids! { + struct Ids { + center_plain, + center_image, + right, + top_right, + top, + top_left, + left, + bottom_left, + bottom, + bottom_right, + } +} + +/// Represents the state of the ImageFrame widget. +pub struct State { + ids: Ids, +} + +impl ImageFrame { + fn new( + edges: [image::Id; 4], + corners: [image::Id; 4], + center: impl Into
, + border_size: impl Into, + ) -> Self { + Self { + common: widget::CommonBuilder::default(), + edges, + edge_src_rects: [None; 4], + corners, + corner_src_rects: [None; 4], + center: center.into(), + border_size: border_size.into(), + color: None, + } + } + + builder_methods! { + pub edge_src_rects { edge_src_rects = [Option; 4] } + pub corner_src_rects { corner_src_rects = [Option; 4] } + } +} + +impl Widget for ImageFrame { + type State = State; + type Style = (); + type Event = (); + + fn init_state(&self, id_gen: widget::id::Generator) -> Self::State { + State { + ids: Ids::new(id_gen), + } + } + + fn style(&self) -> Self::Style { + () + } + + /// Update the state of the ImageFrame + fn update(self, args: widget::UpdateArgs) -> Self::Event { + let widget::UpdateArgs { + id, + state, + rect, + ui, + .. + } = args; + + let (frame_w, frame_h) = rect.w_h(); + + let t_height = self.border_size.top.min(frame_h); + let b_height = self.border_size.bottom.min(frame_h); + let r_width = self.border_size.right.min(frame_w); + let l_width = self.border_size.left.min(frame_w); + let inner_width = (frame_w - r_width - l_width).max(0.0); + let inner_height = (frame_h - t_height - b_height).max(0.0); + + let r_rect = Rect::from_xy_dim( + [rect.x() + (inner_width + r_width) / 2.0, rect.y()], + [r_width, inner_height], + ); + let tr_rect = Rect::from_xy_dim( + [ + rect.x() + (inner_width + r_width) / 2.0, + rect.y() + (inner_height + t_height) / 2.0, + ], + [r_width, t_height], + ); + let t_rect = Rect::from_xy_dim( + [rect.x(), rect.y() + (inner_height + t_height) / 2.0], + [inner_width, t_height], + ); + let tl_rect = Rect::from_xy_dim( + [ + rect.x() - (inner_width + l_width) / 2.0, + rect.y() + (inner_height + t_height) / 2.0, + ], + [l_width, t_height], + ); + let l_rect = Rect::from_xy_dim( + [rect.x() - (inner_width + l_width) / 2.0, rect.y()], + [l_width, inner_height], + ); + let bl_rect = Rect::from_xy_dim( + [ + rect.x() - (inner_width + l_width) / 2.0, + rect.y() - (inner_height + b_height) / 2.0, + ], + [l_width, b_height], + ); + let b_rect = Rect::from_xy_dim( + [rect.x(), rect.y() - (inner_height + b_height) / 2.0], + [inner_width, b_height], + ); + let br_rect = Rect::from_xy_dim( + [ + rect.x() + (inner_width + r_width) / 2.0, + rect.y() - (inner_height + b_height) / 2.0, + ], + [r_width, b_height], + ); + + let maybe_color = self.color; + let set_image = |image_id, rect: Rect, maybe_src_rect, widget_id, ui: &mut UiCell| { + Image::new(image_id) + .xy(rect.xy()) + .wh(rect.dim()) + .parent(id) + .graphics_for(id) + .color(maybe_color) + .set(widget_id, ui); + }; + // Right edge + set_image( + self.edges[2], + r_rect, + self.edge_src_rects[2], + state.ids.right, + ui, + ); + // Top-right corner + set_image( + self.corners[0], + tr_rect, + self.corner_src_rects[0], + state.ids.top_right, + ui, + ); + // Top edge + set_image( + self.edges[0], + t_rect, + self.edge_src_rects[0], + state.ids.top, + ui, + ); + // Top-left corner + set_image( + self.corners[1], + tl_rect, + self.corner_src_rects[1], + state.ids.top_left, + ui, + ); + // Left edge + set_image( + self.edges[3], + l_rect, + self.edge_src_rects[3], + state.ids.left, + ui, + ); + // Bottom-left corner + set_image( + self.corners[3], + bl_rect, + self.corner_src_rects[3], + state.ids.bottom_left, + ui, + ); + // Bottom edge + set_image( + self.edges[1], + b_rect, + self.edge_src_rects[1], + state.ids.bottom, + ui, + ); + // Bottom-right corner + set_image( + self.corners[2], + br_rect, + self.corner_src_rects[2], + state.ids.bottom_right, + ui, + ); + + // Center, + match self.center { + Center::Plain(color) => { + Rectangle::fill_with([inner_width, inner_height], color) + .xy(rect.xy()) + .parent(id) + .graphics_for(id) + .and_then(maybe_color, |w, c| w.color(c)) + .set(state.ids.center_plain, ui); + } + Center::Image(image_id, maybe_src_rect) => { + set_image( + image_id, + Rect::from_xy_dim(rect.xy(), [inner_width, inner_height]), + maybe_src_rect, + state.ids.center_image, + ui, + ); + } + } + } +} + +impl Colorable for ImageFrame { + fn color(mut self, color: Color) -> Self { + self.color = Some(color); + self + } +} diff --git a/voxygen/src/ui/widgets/image_slider.rs b/voxygen/src/ui/widgets/image_slider.rs index daa33d9721..252e780262 100644 --- a/voxygen/src/ui/widgets/image_slider.rs +++ b/voxygen/src/ui/widgets/image_slider.rs @@ -67,7 +67,7 @@ widget_ids! { } } -/// Represents the state of the Slider widget. +/// Represents the state of the ImageSlider widget. pub struct State { ids: Ids, } diff --git a/voxygen/src/ui/widgets/mod.rs b/voxygen/src/ui/widgets/mod.rs index a3987f1a94..3cdc003505 100644 --- a/voxygen/src/ui/widgets/mod.rs +++ b/voxygen/src/ui/widgets/mod.rs @@ -1,3 +1,4 @@ +pub mod image_frame; pub mod image_slider; pub mod ingame; pub mod toggle_button; diff --git a/voxygen/src/ui/widgets/tooltip.rs b/voxygen/src/ui/widgets/tooltip.rs index 81a7240b77..72846d6bed 100644 --- a/voxygen/src/ui/widgets/tooltip.rs +++ b/voxygen/src/ui/widgets/tooltip.rs @@ -274,7 +274,6 @@ impl<'a> Widget for Tooltip<'a> { .graphics_for(id) .parent(id) .color(color) - //.floating(true) .set(state.ids.back_rect, ui); // Title of tooltip @@ -286,7 +285,6 @@ impl<'a> Widget for Tooltip<'a> { .with_style(self.style.title) // Apply transparency .color(style.title.color(ui.theme()).alpha(self.transparency)) - //.floating(true) .set(state.ids.title, ui); // Description of tooltip @@ -298,7 +296,6 @@ impl<'a> Widget for Tooltip<'a> { .with_style(self.style.desc) // Apply transparency .color(style.desc.color(ui.theme()).alpha(self.transparency)) - // .floating(true) .set(state.ids.desc, ui); } }