Add a list mode to inventories to make it easier to see item names at merchants.

This commit is contained in:
Avi Weinstock 2023-01-12 18:26:50 -05:00
parent 494019ecc2
commit 8facf7b58d
10 changed files with 242 additions and 67 deletions

BIN
assets/voxygen/element/ui/bag/buttons/inv_grid.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/ui/bag/buttons/inv_grid_hover.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/ui/bag/buttons/inv_grid_press.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/ui/bag/buttons/inv_list.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/ui/bag/buttons/inv_list_hover.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/ui/bag/buttons/inv_list_press.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
use super::{
cr_color,
cr_color, get_quality_col,
img_ids::{Imgs, ImgsRot},
item_imgs::ItemImgs,
slots::{ArmorSlot, EquipSlot, InventorySlot, SlotManager},
@ -28,7 +28,7 @@ use common::{
use conrod_core::{
color,
widget::{self, Button, Image, Rectangle, Scrollbar, State as ConrodState, Text},
widget_ids, Color, Colorable, Positionable, Scalar, Sizeable, UiCell, Widget, WidgetCommon,
widget_ids, Color, Colorable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
};
use i18n::Localization;
use std::borrow::Cow;
@ -48,6 +48,8 @@ widget_ids! {
inv_scrollbar,
inv_slots_0,
inv_slots[],
inv_slot_names[],
inv_slot_amounts[],
bg,
bg_frame,
char_ico,
@ -89,6 +91,7 @@ pub struct InventoryScroller<'a> {
inventory: &'a Inventory,
bg_ids: &'a BackgroundIds,
show_salvage: bool,
details_mode: bool,
}
impl<'a> InventoryScroller<'a> {
@ -112,6 +115,7 @@ impl<'a> InventoryScroller<'a> {
inventory: &'a Inventory,
bg_ids: &'a BackgroundIds,
show_salvage: bool,
details_mode: bool,
) -> Self {
InventoryScroller {
client,
@ -133,6 +137,7 @@ impl<'a> InventoryScroller<'a> {
inventory,
bg_ids,
show_salvage,
details_mode,
}
}
@ -273,7 +278,8 @@ impl<'a> InventoryScroller<'a> {
.set(state.ids.left_scrollbar_slots, ui);
}
let grid_width = if self.show_bag_inv && !self.on_right {
let grid_width = 362.0;
let grid_height = if self.show_bag_inv && !self.on_right {
440.0 // This for the left bag
} else if self.show_bag_inv && self.on_right {
600.0 // This for the expanded right bag
@ -282,7 +288,7 @@ impl<'a> InventoryScroller<'a> {
};
// Alignment for Grid
Rectangle::fill_with([362.0, grid_width], color::TRANSPARENT)
Rectangle::fill_with([grid_width, grid_height], color::TRANSPARENT)
.bottom_left_with_margins_on(
self.bg_ids.bg_frame,
29.0,
@ -304,6 +310,20 @@ impl<'a> InventoryScroller<'a> {
.resize(self.inventory.capacity(), &mut ui.widget_id_generator());
});
}
if state.ids.inv_slot_names.len() < self.inventory.capacity() {
state.update(|s| {
s.ids
.inv_slot_names
.resize(self.inventory.capacity(), &mut ui.widget_id_generator());
});
}
if state.ids.inv_slot_amounts.len() < self.inventory.capacity() {
state.update(|s| {
s.ids
.inv_slot_amounts
.resize(self.inventory.capacity(), &mut ui.widget_id_generator());
});
}
// Determine the range of inventory slots that are provided by the loadout item
// that the mouse is over
let mouseover_loadout_slots = self
@ -339,9 +359,17 @@ impl<'a> InventoryScroller<'a> {
pulse: self.pulse,
};
for (i, (pos, item)) in self.inventory.slots_with_id().enumerate() {
let x = i % 9;
let y = i / 9;
let mut i = 0;
for (pos, item) in self.inventory.slots_with_id() {
if self.details_mode && !self.is_us && matches!(item, None) {
continue;
}
let (x, y) = if self.details_mode {
(0, i)
} else {
(i % 9, i / 9)
};
let slot_size = if self.details_mode { 20.0 } else { 40.0 };
// Slot
let mut slot_widget = slot_maker
@ -351,12 +379,12 @@ impl<'a> InventoryScroller<'a> {
ours: self.is_us,
entity: self.entity,
},
[40.0; 2],
[slot_size as f32; 2],
)
.top_left_with_margins_on(
state.ids.inv_alignment,
0.0 + y as f64 * (40.0),
0.0 + x as f64 * (40.0),
0.0 + y as f64 * slot_size,
0.0 + x as f64 * slot_size,
);
// Highlight slots are provided by the loadout item that the mouse is over
@ -420,9 +448,33 @@ impl<'a> InventoryScroller<'a> {
)
.set(state.ids.inv_slots[i], ui);
}
if self.details_mode {
Text::new(&item.name())
.top_left_with_margins_on(
state.ids.inv_alignment,
0.0 + y as f64 * slot_size,
30.0 + x as f64 * slot_size,
)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(14))
.color(get_quality_col(item))
.set(state.ids.inv_slot_names[i], ui);
Text::new(&format!("{}", item.amount()))
.top_left_with_margins_on(
state.ids.inv_alignment,
0.0 + y as f64 * slot_size,
grid_width - 40.0 + x as f64 * slot_size,
)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(14))
.color(get_quality_col(item))
.set(state.ids.inv_slot_amounts[i], ui);
}
} else {
slot_widget.set(state.ids.inv_slots[i], ui);
}
i += 1;
}
}
@ -529,6 +581,7 @@ widget_ids! {
tab_3,
tab_4,
bag_expand_btn,
bag_details_btn,
// Armor Slots
slots_bg,
head_slot,
@ -654,6 +707,7 @@ pub enum Event {
Close,
SortInventory,
SwapEquippedWeapons,
SetDetailsMode(bool),
}
impl<'a> Widget for Bag<'a> {
@ -768,6 +822,7 @@ impl<'a> Widget for Bag<'a> {
inventory,
&state.bg_ids,
self.show.crafting_fields.salvage,
self.show.bag_details,
)
.set(state.ids.inventory_scroller, ui);
@ -776,49 +831,73 @@ impl<'a> Widget for Bag<'a> {
.w_h(40.0, 37.0)
.top_left_with_margins_on(state.bg_ids.bg, 4.0, 2.0)
.set(state.ids.char_ico, ui);
// Button to expand bag
let txt = if self.show.bag_inv {
"Show Loadout"
let buttons_top = if self.show.bag_inv { 53.0 } else { 460.0 };
let (txt, btn, hover, press) = if self.show.bag_details {
(
"Grid mode",
self.imgs.grid_btn,
self.imgs.grid_btn_hover,
self.imgs.grid_btn_press,
)
} else {
"Expand Bag"
(
"List mode",
self.imgs.list_btn,
self.imgs.list_btn_hover,
self.imgs.list_btn_press,
)
};
let expand_btn = Button::image(if self.show.bag_inv {
self.imgs.collapse_btn
let details_btn = Button::image(btn)
.w_h(32.0, 17.0)
.hover_image(hover)
.press_image(press);
if details_btn
.mid_top_with_margin_on(state.bg_ids.bg_frame, buttons_top)
.with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
.set(state.ids.bag_details_btn, ui)
.was_clicked()
{
event = Some(Event::SetDetailsMode(!self.show.bag_details));
}
// Button to expand bag
let (txt, btn, hover, press) = if self.show.bag_inv {
(
"Show Loadout",
self.imgs.collapse_btn,
self.imgs.collapse_btn_hover,
self.imgs.collapse_btn_press,
)
} else {
self.imgs.expand_btn
})
.w_h(30.0, 17.0)
.hover_image(if self.show.bag_inv {
self.imgs.collapse_btn_hover
} else {
self.imgs.expand_btn_hover
})
.press_image(if self.show.bag_inv {
self.imgs.collapse_btn_press
} else {
self.imgs.expand_btn_press
});
(
"Expand Bag",
self.imgs.expand_btn,
self.imgs.expand_btn_hover,
self.imgs.expand_btn_press,
)
};
let expand_btn = Button::image(btn)
.w_h(30.0, 17.0)
.hover_image(hover)
.press_image(press);
// Only show expand button when it's needed...
if inventory.slots().count() > 45 || self.show.bag_inv {
let expand_btn_top = if self.show.bag_inv { 53.0 } else { 460.0 };
if expand_btn
.top_right_with_margins_on(state.bg_ids.bg_frame, expand_btn_top, 37.0)
if (inventory.slots().count() > 45 || self.show.bag_inv)
&& expand_btn
.top_right_with_margins_on(state.bg_ids.bg_frame, buttons_top, 37.0)
.with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
.set(state.ids.bag_expand_btn, ui)
.was_clicked()
{
event = Some(Event::BagExpand);
}
{
event = Some(Event::BagExpand);
}
// Sort inventory button
let inv_sort_btn_top: Scalar = if !self.show.bag_inv { 460.0 } else { 53.0 };
if Button::image(self.imgs.inv_sort_btn)
.w_h(30.0, 17.0)
.hover_image(self.imgs.inv_sort_btn_hover)
.press_image(self.imgs.inv_sort_btn_press)
.top_left_with_margins_on(state.bg_ids.bg_frame, inv_sort_btn_top, 47.0)
.top_left_with_margins_on(state.bg_ids.bg_frame, buttons_top, 47.0)
.with_tooltip(
self.tooltip_manager,
&(match inventory.next_sort_order() {

View File

@ -565,6 +565,12 @@ image_ids! {
edit_btn_press: "voxygen.element.ui.char_select.icons.pen_press",
// Inventory
grid_btn: "voxygen.element.ui.bag.buttons.inv_grid",
grid_btn_hover: "voxygen.element.ui.bag.buttons.inv_grid_hover",
grid_btn_press: "voxygen.element.ui.bag.buttons.inv_grid_press",
list_btn: "voxygen.element.ui.bag.buttons.inv_list",
list_btn_hover: "voxygen.element.ui.bag.buttons.inv_list_hover",
list_btn_press: "voxygen.element.ui.bag.buttons.inv_list_press",
collapse_btn: "voxygen.element.ui.bag.buttons.inv_collapse",
collapse_btn_hover: "voxygen.element.ui.bag.buttons.inv_collapse_hover",
collapse_btn_press: "voxygen.element.ui.bag.buttons.inv_collapse_press",

View File

@ -160,7 +160,7 @@ const BUFF_COLOR: Color = Color::Rgba(0.06, 0.69, 0.12, 1.0);
const DEBUFF_COLOR: Color = Color::Rgba(0.79, 0.19, 0.17, 1.0);
// Item Quality Colors
const QUALITY_LOW: Color = Color::Rgba(0.41, 0.41, 0.41, 1.0); // Grey - Trash, can be sold to vendors
const QUALITY_LOW: Color = Color::Rgba(0.60, 0.60, 0.60, 1.0); // Grey - Trash, can be sold to vendors
const QUALITY_COMMON: Color = Color::Rgba(0.79, 1.00, 1.00, 1.0); // Light blue - Crafting mats, food, starting equipment, quest items (like keys), rewards for easy quests
const QUALITY_MODERATE: Color = Color::Rgba(0.06, 0.69, 0.12, 1.0); // Green - Quest Rewards, commonly looted items from NPCs
const QUALITY_HIGH: Color = Color::Rgba(0.18, 0.32, 0.9, 1.0); // Blue - Dungeon rewards, boss loot, rewards for hard quests
@ -881,7 +881,9 @@ pub struct Show {
crafting: bool,
bag: bool,
bag_inv: bool,
bag_details: bool,
trade: bool,
trade_details: bool,
social: bool,
diary: bool,
group: bool,
@ -1374,7 +1376,9 @@ impl Hud {
intro: false,
bag: false,
bag_inv: false,
bag_details: false,
trade: false,
trade_details: false,
esc_menu: false,
open_windows: Windows::None,
map: false,
@ -3066,6 +3070,7 @@ impl Hud {
.set(self.ids.bag, ui_widgets)
{
Some(bag::Event::BagExpand) => self.show.bag_inv = !self.show.bag_inv,
Some(bag::Event::SetDetailsMode(mode)) => self.show.bag_details = mode,
Some(bag::Event::Close) => {
self.show.stats = false;
Self::show_bag(&mut self.slot_manager, &mut self.show, false);
@ -3093,6 +3098,7 @@ impl Hud {
&self.item_imgs,
&self.fonts,
&self.rot_imgs,
tooltip_manager,
item_tooltip_manager,
&mut self.slot_manager,
i18n,
@ -3103,7 +3109,7 @@ impl Hud {
.set(self.ids.trade, ui_widgets)
{
match action {
Err(update) => match update {
trade::TradeEvent::HudUpdate(update) => match update {
trade::HudUpdate::Focus(idx) => self.to_focus = Some(Some(idx)),
trade::HudUpdate::Submit => {
let key = self.show.trade_amount_input_key.take();
@ -3114,7 +3120,7 @@ impl Hud {
});
},
},
Ok(action) => {
trade::TradeEvent::TradeAction(action) => {
if let TradeAction::Decline = action {
self.show.stats = false;
self.show.trade(false);
@ -3127,6 +3133,9 @@ impl Hud {
}
events.push(Event::TradeAction(action));
},
trade::TradeEvent::SetDetailsMode(mode) => {
self.show.trade_details = mode;
},
}
}
}

View File

@ -23,7 +23,8 @@ use crate::{
ui::{
fonts::Fonts,
slot::{ContentSize, SlotMaker},
ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable,
ImageFrame, ItemTooltip, ItemTooltipManager, ItemTooltipable, Tooltip, TooltipManager,
Tooltipable,
},
};
@ -35,6 +36,13 @@ use super::{
};
use std::borrow::Cow;
#[derive(Debug)]
pub enum TradeEvent {
TradeAction(TradeAction),
SetDetailsMode(bool),
HudUpdate(HudUpdate),
}
#[derive(Debug)]
pub enum HudUpdate {
Focus(widget::Id),
@ -69,6 +77,7 @@ widget_ids! {
amount_open_ovlay,
amount_input,
amount_btn,
trade_details_btn,
}
}
@ -80,6 +89,7 @@ pub struct Trade<'a> {
item_imgs: &'a ItemImgs,
fonts: &'a Fonts,
rot_imgs: &'a ImgsRot,
tooltip_manager: &'a mut TooltipManager,
item_tooltip_manager: &'a mut ItemTooltipManager,
#[conrod(common_builder)]
common: widget::CommonBuilder,
@ -98,6 +108,7 @@ impl<'a> Trade<'a> {
item_imgs: &'a ItemImgs,
fonts: &'a Fonts,
rot_imgs: &'a ImgsRot,
tooltip_manager: &'a mut TooltipManager,
item_tooltip_manager: &'a mut ItemTooltipManager,
slot_manager: &'a mut SlotManager,
localized_strings: &'a Localization,
@ -112,6 +123,7 @@ impl<'a> Trade<'a> {
item_imgs,
fonts,
rot_imgs,
tooltip_manager,
item_tooltip_manager,
common: widget::CommonBuilder::default(),
slot_manager,
@ -187,7 +199,8 @@ impl<'a> Trade<'a> {
trade: &'a PendingTrade,
prices: &'a Option<SitePrices>,
ours: bool,
) -> Option<TradeAction> {
) -> Option<TradeEvent> {
let mut event = None;
let inventories = self.client.inventories();
let check_if_us = |who: usize| -> Option<_> {
let uid = trade.parties[who];
@ -273,22 +286,24 @@ impl<'a> Trade<'a> {
.collect();
if matches!(trade.phase(), TradePhase::Mutate) {
self.phase1_itemwidget(
state,
ui,
inventory,
who,
ours,
entity,
name,
prices,
&tradeslots,
);
event = self
.phase1_itemwidget(
state,
ui,
inventory,
who,
ours,
entity,
name,
prices,
&tradeslots,
)
.or(event);
} else {
self.phase2_itemwidget(state, ui, inventory, who, ours, entity, &tradeslots);
}
None
event
}
fn phase1_itemwidget(
@ -302,7 +317,8 @@ impl<'a> Trade<'a> {
name: String,
prices: &'a Option<SitePrices>,
tradeslots: &[TradeSlot],
) {
) -> Option<TradeEvent> {
let mut event = None;
// Tooltips
let item_tooltip = ItemTooltip::new(
{
@ -351,8 +367,56 @@ impl<'a> Trade<'a> {
inventory,
&state.bg_ids,
false,
self.show.trade_details,
)
.set(state.ids.inventory_scroller, ui);
let bag_tooltip = Tooltip::new({
// 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(self.fonts.cyri.scale(15))
.parent(ui.window)
.desc_font_size(self.fonts.cyri.scale(12))
.font_id(self.fonts.cyri.conrod_id)
.desc_text_color(TEXT_COLOR);
let buttons_top = 53.0;
let (txt, btn, hover, press) = if self.show.trade_details {
(
"Grid mode",
self.imgs.grid_btn,
self.imgs.grid_btn_hover,
self.imgs.grid_btn_press,
)
} else {
(
"List mode",
self.imgs.list_btn,
self.imgs.list_btn_hover,
self.imgs.list_btn_press,
)
};
let details_btn = Button::image(btn)
.w_h(32.0, 17.0)
.hover_image(hover)
.press_image(press);
if details_btn
.mid_top_with_margin_on(state.bg_ids.bg_frame, buttons_top)
.with_tooltip(self.tooltip_manager, txt, "", &bag_tooltip, TEXT_COLOR)
.set(state.ids.trade_details_btn, ui)
.was_clicked()
{
event = Some(TradeEvent::SetDetailsMode(!self.show.trade_details));
}
}
let mut slot_maker = SlotMaker {
@ -428,6 +492,7 @@ impl<'a> Trade<'a> {
slot_widget.set(slot_id, ui);
}
}
event
}
fn phase2_itemwidget(
@ -495,7 +560,7 @@ impl<'a> Trade<'a> {
state: &mut ConrodState<'_, State>,
ui: &mut UiCell<'_>,
trade: &'a PendingTrade,
) -> Option<TradeAction> {
) -> Option<TradeEvent> {
let mut event = None;
let (hover_img, press_img, accept_button_luminance) = if trade.is_empty_trade() {
//Darken the accept button if the trade is empty.
@ -543,7 +608,7 @@ impl<'a> Trade<'a> {
{
event = Some(TradeAction::Decline);
}
event
event.map(TradeEvent::TradeAction)
}
fn input_item_amount(
@ -551,7 +616,7 @@ impl<'a> Trade<'a> {
state: &mut ConrodState<'_, State>,
ui: &mut UiCell<'_>,
trade: &'a PendingTrade,
) -> Option<HudUpdate> {
) -> Option<TradeEvent> {
let mut event = None;
let selected = self.slot_manager.selected().and_then(|s| match s {
SlotKind::Trade(t_s) => t_s.invslot.and_then(|slot| {
@ -671,14 +736,14 @@ impl<'a> Trade<'a> {
.color(TEXT_GRAY_COLOR.alpha(0.25))
.set(state.ids.amount_notice, ui);
}
event
event.map(TradeEvent::HudUpdate)
}
fn close_button(
&mut self,
state: &mut ConrodState<'_, State>,
ui: &mut UiCell<'_>,
) -> Option<TradeAction> {
) -> Option<TradeEvent> {
if Button::image(self.imgs.close_btn)
.w_h(24.0, 25.0)
.hover_image(self.imgs.close_btn_hover)
@ -687,7 +752,7 @@ impl<'a> Trade<'a> {
.set(state.ids.trade_close, ui)
.was_clicked()
{
Some(TradeAction::Decline)
Some(TradeEvent::TradeAction(TradeAction::Decline))
} else {
None
}
@ -695,7 +760,7 @@ impl<'a> Trade<'a> {
}
impl<'a> Widget for Trade<'a> {
type Event = Option<Result<TradeAction, HudUpdate>>;
type Event = Option<TradeEvent>;
type State = State;
type Style = ();
@ -718,7 +783,7 @@ impl<'a> Widget for Trade<'a> {
let mut event = None;
let (trade, prices) = match self.client.pending_trade() {
Some((_, trade, prices)) => (trade, prices),
None => return Some(Ok(TradeAction::Decline)),
None => return Some(TradeEvent::TradeAction(TradeAction::Decline)),
};
if state.ids.inv_alignment.len() < 2 {
@ -747,8 +812,6 @@ impl<'a> Widget for Trade<'a> {
event = self.item_pane(state, ui, trade, prices, true).or(event);
event = self.accept_decline_buttons(state, ui, trade).or(event);
event = self.close_button(state, ui).or(event);
self.input_item_amount(state, ui, trade)
.map(Err)
.or_else(|| event.map(Ok))
self.input_item_amount(state, ui, trade).or(event)
}
}