Scale content image based on slot size, fix weapon images not showing, stop selection of empty slots

This commit is contained in:
Imbris 2020-04-05 20:03:59 -04:00 committed by Pfauenauge90
parent 4c5f668203
commit 77948d27ca
3 changed files with 91 additions and 58 deletions

View File

@ -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(

View File

@ -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,
};

View File

@ -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>, K: SlotKinds> {
pub empty_slot: image::Id,
pub filled_slot: image::Id,
pub selected_slot: image::Id,
// Is this useful?
pub background_color: Option<Color>,
pub content_size: Vec2<f32>,
pub selected_content_size: Vec2<f32>,
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<f32>,
@ -44,14 +52,29 @@ where
C: ContentKey + Into<K>,
K: SlotKinds,
{
pub fn fabricate(&mut self, contents: C) -> Slot<C, K> {
pub fn fabricate(&mut self, contents: C, wh: [f32; 2]) -> Slot<C, K> {
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>, K: SlotKinds> {
// Size of content image
content_size: Vec2<f32>,
// TODO: maybe use constant scale factor or move this setting to the slot manager?
selected_content_size: Vec2<f32>,
selected_content_scale: f32,
icon: Option<(image::Id, Vec2<f32>, Option<Color>)>,
@ -280,7 +318,7 @@ where
filled_slot: image::Id,
selected_slot: image::Id,
content_size: Vec2<f32>,
selected_content_size: Vec2<f32>,
selected_content_scale: f32,
amount_font: font::Id,
amount_font_size: u32,
amount_margins: Vec2<f32>,
@ -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)