From 77948d27ca67f10441041d6f6f87b3e812e7a13f Mon Sep 17 00:00:00 2001 From: Imbris Date: Sun, 5 Apr 2020 20:03:59 -0400 Subject: [PATCH] Scale content image based on slot size, fix weapon images not showing, stop selection of empty slots --- voxygen/src/hud/bag.rs | 66 ++++++++++++--------------- voxygen/src/hud/slot_kinds.rs | 2 + voxygen/src/ui/widgets/slot.rs | 81 +++++++++++++++++++++++++--------- 3 files changed, 91 insertions(+), 58 deletions(-) diff --git a/voxygen/src/hud/bag.rs b/voxygen/src/hud/bag.rs index 22d92e3898..60a74ac19f 100644 --- a/voxygen/src/hud/bag.rs +++ b/voxygen/src/hud/bag.rs @@ -8,8 +8,9 @@ use super::{ use crate::{ i18n::VoxygenLocalization, ui::{ - fonts::ConrodVoxygenFonts, slot::SlotMaker, ImageFrame, Tooltip, TooltipManager, - Tooltipable, + fonts::ConrodVoxygenFonts, + slot::{ContentSize, SlotMaker}, + ImageFrame, Tooltip, TooltipManager, Tooltipable, }, }; use client::Client; @@ -318,8 +319,11 @@ impl<'a> Widget for Bag<'a> { filled_slot: self.imgs.armor_slot, selected_slot: self.imgs.armor_slot_sel, background_color: Some(UI_HIGHLIGHT_0), - content_size: Vec2::broadcast(30.0), - selected_content_size: Vec2::broadcast(32.0), + content_size: ContentSize { + width_height_ratio: 1.0, + max_fraction: 0.75, + }, + selected_content_scale: 1.067, amount_font: self.fonts.cyri.conrod_id, amount_margins: Vec2::new(-4.0, 0.0), amount_font_size: self.fonts.cyri.scale(12), @@ -331,8 +335,7 @@ impl<'a> Widget for Bag<'a> { // Head let (title, desc) = ("Helmet", ""); slot_maker - .fabricate(ArmorSlot::Head) - .w_h(45.0, 45.0) + .fabricate(ArmorSlot::Head, [45.0; 2]) .mid_top_with_margin_on(state.ids.bg_frame, 60.0) .with_icon(self.imgs.head_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -340,8 +343,7 @@ impl<'a> Widget for Bag<'a> { // Necklace let (title, desc) = ("Neck", ""); slot_maker - .fabricate(ArmorSlot::Neck) - .w_h(45.0, 45.0) + .fabricate(ArmorSlot::Neck, [45.0; 2]) .mid_bottom_with_margin_on(state.ids.head_slot, -55.0) .with_icon(self.imgs.necklace_bg, Vec2::new(40.0, 31.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -353,8 +355,7 @@ impl<'a> Widget for Bag<'a> { .as_ref() .map_or(("Chest", ""), |item| (item.name(), item.description())); slot_maker - .fabricate(ArmorSlot::Chest) - .w_h(85.0, 85.0) + .fabricate(ArmorSlot::Chest, [85.0; 2]) .mid_bottom_with_margin_on(state.ids.neck_slot, -95.0) .with_icon(self.imgs.chest_bg, Vec2::new(64.0, 42.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -365,8 +366,7 @@ impl<'a> Widget for Bag<'a> { .as_ref() .map_or(("Shoulders", ""), |item| (item.name(), item.description())); slot_maker - .fabricate(ArmorSlot::Shoulders) - .w_h(70.0, 70.0) + .fabricate(ArmorSlot::Shoulders, [70.0; 2]) .bottom_left_with_margins_on(state.ids.chest_slot, 0.0, -80.0) .with_icon(self.imgs.shoulders_bg, Vec2::new(60.0, 36.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -377,8 +377,7 @@ impl<'a> Widget for Bag<'a> { .as_ref() .map_or(("Hands", ""), |item| (item.name(), item.description())); slot_maker - .fabricate(ArmorSlot::Hands) - .w_h(70.0, 70.0) + .fabricate(ArmorSlot::Hands, [70.0; 2]) .bottom_right_with_margins_on(state.ids.chest_slot, 0.0, -80.0) .with_icon(self.imgs.hands_bg, Vec2::new(55.0, 60.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -389,8 +388,7 @@ impl<'a> Widget for Bag<'a> { .as_ref() .map_or(("Belt", ""), |item| (item.name(), item.description())); slot_maker - .fabricate(ArmorSlot::Belt) - .w_h(45.0, 45.0) + .fabricate(ArmorSlot::Belt, [45.0; 2]) .mid_bottom_with_margin_on(state.ids.chest_slot, -55.0) .with_icon(self.imgs.belt_bg, Vec2::new(40.0, 23.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -401,8 +399,7 @@ impl<'a> Widget for Bag<'a> { .as_ref() .map_or(("Legs", ""), |item| (item.name(), item.description())); slot_maker - .fabricate(ArmorSlot::Legs) - .w_h(85.0, 85.0) + .fabricate(ArmorSlot::Legs, [85.0; 2]) .mid_bottom_with_margin_on(state.ids.belt_slot, -95.0) .with_icon(self.imgs.legs_bg, Vec2::new(48.0, 70.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -410,8 +407,7 @@ impl<'a> Widget for Bag<'a> { // Ring-L let (title, desc) = ("Left Ring", ""); slot_maker - .fabricate(ArmorSlot::LeftRing) - .w_h(45.0, 45.0) + .fabricate(ArmorSlot::LeftRing, [45.0; 2]) .bottom_right_with_margins_on(state.ids.shoulders_slot, -55.0, 0.0) .with_icon(self.imgs.ring_l_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -419,8 +415,7 @@ impl<'a> Widget for Bag<'a> { // Ring-R let (title, desc) = ("Right Ring", ""); slot_maker - .fabricate(ArmorSlot::RightRing) - .w_h(45.0, 45.0) + .fabricate(ArmorSlot::RightRing, [45.0; 2]) .bottom_left_with_margins_on(state.ids.hands_slot, -55.0, 0.0) .with_icon(self.imgs.ring_r_bg, Vec2::new(36.0, 40.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -428,8 +423,7 @@ impl<'a> Widget for Bag<'a> { // Back let (title, desc) = ("Back", ""); slot_maker - .fabricate(ArmorSlot::Back) - .w_h(45.0, 45.0) + .fabricate(ArmorSlot::Back, [45.0; 2]) .down_from(state.ids.ring_l_slot, 10.0) .with_icon(self.imgs.back_bg, Vec2::new(33.0, 40.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -440,8 +434,7 @@ impl<'a> Widget for Bag<'a> { .as_ref() .map_or(("Feet", ""), |item| (item.name(), item.description())); slot_maker - .fabricate(ArmorSlot::Feet) - .w_h(45.0, 45.0) + .fabricate(ArmorSlot::Feet, [45.0; 2]) .down_from(state.ids.ring_r_slot, 10.0) .with_icon(self.imgs.feet_bg, Vec2::new(32.0, 40.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -449,8 +442,7 @@ impl<'a> Widget for Bag<'a> { // Tabard let (title, desc) = ("Tabard", ""); slot_maker - .fabricate(ArmorSlot::Tabard) - .w_h(70.0, 70.0) + .fabricate(ArmorSlot::Tabard, [70.0; 2]) .top_right_with_margins_on(state.ids.bg_frame, 80.5, 53.0) .with_icon(self.imgs.tabard_bg, Vec2::new(60.0, 60.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -462,8 +454,7 @@ impl<'a> Widget for Bag<'a> { .map(|i| &i.item) .map_or(("Mainhand", ""), |item| (item.name(), item.description())); slot_maker - .fabricate(ArmorSlot::Mainhand) - .w_h(85.0, 85.0) + .fabricate(ArmorSlot::Mainhand, [85.0; 2]) .bottom_right_with_margins_on(state.ids.back_slot, -95.0, 0.0) .with_icon(self.imgs.mainhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -475,8 +466,7 @@ impl<'a> Widget for Bag<'a> { .map(|i| &i.item) .map_or(("Offhand", ""), |item| (item.name(), item.description())); slot_maker - .fabricate(ArmorSlot::Offhand) - .w_h(85.0, 85.0) + .fabricate(ArmorSlot::Offhand, [85.0; 2]) .bottom_left_with_margins_on(state.ids.feet_slot, -95.0, 0.0) .with_icon(self.imgs.offhand_bg, Vec2::new(75.0, 75.0), Some(UI_MAIN)) .with_tooltip(self.tooltip_manager, title, desc, &item_tooltip) @@ -601,8 +591,11 @@ impl<'a> Widget for Bag<'a> { filled_slot: self.imgs.inv_slot, selected_slot: self.imgs.inv_slot_sel, background_color: Some(UI_MAIN), - content_size: Vec2::broadcast(30.0), - selected_content_size: Vec2::broadcast(32.0), + content_size: ContentSize { + width_height_ratio: 1.0, + max_fraction: 0.75, + }, + selected_content_scale: 1.067, amount_font: self.fonts.cyri.conrod_id, amount_margins: Vec2::new(-4.0, 0.0), amount_font_size: self.fonts.cyri.scale(12), @@ -617,13 +610,12 @@ impl<'a> Widget for Bag<'a> { // Slot let slot_widget = slot_maker - .fabricate(InventorySlot(i)) + .fabricate(InventorySlot(i), [40.0; 2]) .top_left_with_margins_on( state.ids.inv_alignment, 0.0 + y as f64 * (40.0), 0.0 + x as f64 * (40.0), - ) - .wh([40.0; 2]); + ); if let Some(item) = item { slot_widget .with_tooltip( diff --git a/voxygen/src/hud/slot_kinds.rs b/voxygen/src/hud/slot_kinds.rs index 900342e324..017c31c3df 100644 --- a/voxygen/src/hud/slot_kinds.rs +++ b/voxygen/src/hud/slot_kinds.rs @@ -86,6 +86,8 @@ impl ContentKey for ArmorSlot { ArmorSlot::Hands => source.hand.as_ref(), ArmorSlot::Legs => source.pants.as_ref(), ArmorSlot::Feet => source.foot.as_ref(), + ArmorSlot::Mainhand => source.active_item.as_ref().map(|i| &i.item), + ArmorSlot::Offhand => source.second_item.as_ref().map(|i| &i.item), _ => None, }; diff --git a/voxygen/src/ui/widgets/slot.rs b/voxygen/src/ui/widgets/slot.rs index 69b8ee3bef..43735d114d 100644 --- a/voxygen/src/ui/widgets/slot.rs +++ b/voxygen/src/ui/widgets/slot.rs @@ -22,14 +22,22 @@ pub trait ContentKey: Copy { pub trait SlotKinds: Sized + PartialEq + Copy {} +pub struct ContentSize { + // Width divided by height + pub width_height_ratio: f32, + // Max fraction of slot widget size that each side can be + pub max_fraction: f32, +} + pub struct SlotMaker<'a, C: ContentKey + Into, K: SlotKinds> { pub empty_slot: image::Id, pub filled_slot: image::Id, pub selected_slot: image::Id, // Is this useful? pub background_color: Option, - pub content_size: Vec2, - pub selected_content_size: Vec2, + pub content_size: ContentSize, + // How to scale content size relative to base content size when selected + pub selected_content_scale: f32, pub amount_font: font::Id, pub amount_font_size: u32, pub amount_margins: Vec2, @@ -44,14 +52,29 @@ where C: ContentKey + Into, K: SlotKinds, { - pub fn fabricate(&mut self, contents: C) -> Slot { + pub fn fabricate(&mut self, contents: C, wh: [f32; 2]) -> Slot { + let content_size = { + let ContentSize { + max_fraction, + width_height_ratio, + } = self.content_size; + let w_max = max_fraction * wh[0]; + let h_max = max_fraction * wh[1]; + let max_ratio = w_max / h_max; + let (w, h) = if max_ratio > width_height_ratio { + (width_height_ratio * h_max, w_max) + } else { + (w_max, w_max / width_height_ratio) + }; + Vec2::new(w, h) + }; Slot::new( contents, self.empty_slot, self.filled_slot, self.selected_slot, - self.content_size, - self.selected_content_size, + content_size, + self.selected_content_scale, self.amount_font, self.amount_font_size, self.amount_margins, @@ -59,6 +82,7 @@ where self.content_source, self.image_source, ) + .wh([wh[0] as f64, wh[1] as f64]) .and_then(self.background_color, |s, c| s.with_background_color(c)) .and_then(self.slot_manager.as_mut(), |s, m| s.with_manager(m)) } @@ -150,7 +174,23 @@ where std::mem::replace(&mut self.events, Vec::new()) } - fn update(&mut self, widget: widget::Id, slot: K, ui: &conrod_core::Ui) -> Interaction { + fn update( + &mut self, + widget: widget::Id, + slot: K, + ui: &conrod_core::Ui, + filled: bool, + ) -> Interaction { + // If the slot is no longer filled deselect it or cancel dragging + match &self.state { + ManagerState::Selected(id, _) | ManagerState::Dragging(id, _) + if *id == widget && !filled => + { + self.state = ManagerState::Idle; + } + _ => (), + } + // If this is the selected/dragged widget make sure the slot value is up to date match &mut self.state { ManagerState::Selected(id, stored_slot) | ManagerState::Dragging(id, stored_slot) @@ -163,7 +203,6 @@ where // TODO: make more robust wrt multiple events in the same frame (eg event order // may matter) TODO: handle taps as well - // TODO: handle clicks in empty space // TODO: handle drags let click_count = ui.widget_input(widget).clicks().left().count(); if click_count > 0 { @@ -192,7 +231,7 @@ where } } else { // No widgets were selected - if odd_num_clicks { + if odd_num_clicks && filled { ManagerState::Selected(widget, slot) } else { // Selected and then deselected with one or more clicks @@ -222,8 +261,7 @@ pub struct Slot<'a, C: ContentKey + Into, K: SlotKinds> { // Size of content image content_size: Vec2, - // TODO: maybe use constant scale factor or move this setting to the slot manager? - selected_content_size: Vec2, + selected_content_scale: f32, icon: Option<(image::Id, Vec2, Option)>, @@ -280,7 +318,7 @@ where filled_slot: image::Id, selected_slot: image::Id, content_size: Vec2, - selected_content_size: Vec2, + selected_content_scale: f32, amount_font: font::Id, amount_font_size: u32, amount_margins: Vec2, @@ -295,7 +333,7 @@ where selected_slot, background_color: None, content_size, - selected_content_size, + selected_content_scale, icon: None, amount_font, amount_font_size, @@ -343,7 +381,7 @@ where selected_slot, background_color, content_size, - selected_content_size, + selected_content_scale, icon, amount_font, amount_font_size, @@ -366,9 +404,9 @@ where } // Get whether this slot is selected - let interaction = self - .slot_manager - .map_or(Interaction::None, |m| m.update(id, content.into(), ui)); + let interaction = self.slot_manager.map_or(Interaction::None, |m| { + m.update(id, content.into(), ui, state.cached_image.is_some()) + }); // Get image ids let content_image = state.cached_image.as_ref().map(|c| c.1); @@ -412,11 +450,12 @@ where if let Some(content_image) = content_image { Image::new(content_image) .x_y(x, y) - .wh(if let Interaction::Selected = interaction { - selected_content_size - } else { - content_size - } + .wh((content_size + * if let Interaction::Selected = interaction { + selected_content_scale + } else { + 1.0 + }) .map(|e| e as f64) .into_array()) .parent(id)