mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'jh0l/trading_amount_input' into 'master'
feat: ✨ an input box in the trade window so that users can set an exact amount See merge request veloren/veloren!3261
This commit is contained in:
commit
f96b803f63
@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Hostile agent will now abort pursuing their target based on multiple metrics
|
||||
- Admin command to reload all chunks on the server
|
||||
- Furniture and waypoints in site2 towns
|
||||
- text input for trading
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -23,10 +23,10 @@
|
||||
"hud.trade.tooltip_hint_2": "<Ctrl-Click to Auto-Balance with this.>",
|
||||
"hud.trade.your_offer": "Your offer",
|
||||
"hud.trade.their_offer": "Their offer",
|
||||
"hud.trade.amount_input": "Select an item"
|
||||
},
|
||||
|
||||
|
||||
vector_map: {
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::{
|
||||
comp::inventory::{slot::InvSlotId, trade_pricing::TradePricing, Inventory},
|
||||
terrain::BiomeKind,
|
||||
@ -94,6 +96,24 @@ impl TradePhase {
|
||||
}
|
||||
}
|
||||
|
||||
impl TradeAction {
|
||||
pub fn item(item: InvSlotId, delta: i32, ours: bool) -> Option<Self> {
|
||||
match delta.cmp(&0) {
|
||||
Ordering::Equal => None,
|
||||
Ordering::Less => Some(TradeAction::RemoveItem {
|
||||
item,
|
||||
ours,
|
||||
quantity: -delta as u32,
|
||||
}),
|
||||
Ordering::Greater => Some(TradeAction::AddItem {
|
||||
item,
|
||||
ours,
|
||||
quantity: delta as u32,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PendingTrade {
|
||||
pub fn new(party: Uid, counterparty: Uid) -> PendingTrade {
|
||||
PendingTrade {
|
||||
|
@ -448,6 +448,12 @@ image_ids! {
|
||||
search_btn: "voxygen.element.ui.generic.buttons.search_btn",
|
||||
search_btn_hover: "voxygen.element.ui.generic.buttons.search_btn_hover",
|
||||
search_btn_press: "voxygen.element.ui.generic.buttons.search_btn_press",
|
||||
|
||||
// Edit-Button
|
||||
edit_btn: "voxygen.element.ui.char_select.icons.pen",
|
||||
edit_btn_hover: "voxygen.element.ui.char_select.icons.pen_hover",
|
||||
edit_btn_press: "voxygen.element.ui.char_select.icons.pen_press",
|
||||
|
||||
// Inventory
|
||||
collapse_btn: "voxygen.element.ui.bag.buttons.inv_collapse",
|
||||
collapse_btn_hover: "voxygen.element.ui.bag.buttons.inv_collapse_hover",
|
||||
|
@ -645,6 +645,34 @@ pub struct MapMarkers {
|
||||
group: HashMap<Uid, Vec2<i32>>,
|
||||
}
|
||||
|
||||
/// (target slot, input value, inventory quantity, is our inventory, error,
|
||||
/// trade.offers index of trade slot)
|
||||
pub struct TradeAmountInput {
|
||||
slot: InvSlotId,
|
||||
input: String,
|
||||
inv: u32,
|
||||
ours: bool,
|
||||
err: Option<String>,
|
||||
who: usize,
|
||||
input_painted: bool,
|
||||
submit_action: Option<TradeAction>,
|
||||
}
|
||||
|
||||
impl TradeAmountInput {
|
||||
pub fn new(slot: InvSlotId, input: String, inv: u32, ours: bool, who: usize) -> Self {
|
||||
Self {
|
||||
slot,
|
||||
input,
|
||||
inv,
|
||||
ours,
|
||||
who,
|
||||
err: None,
|
||||
input_painted: false,
|
||||
submit_action: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Show {
|
||||
ui: bool,
|
||||
intro: bool,
|
||||
@ -676,6 +704,7 @@ pub struct Show {
|
||||
prompt_dialog: Option<PromptDialogSettings>,
|
||||
location_markers: MapMarkers,
|
||||
salvage: bool,
|
||||
trade_amount_input_key: Option<TradeAmountInput>,
|
||||
}
|
||||
impl Show {
|
||||
fn bag(&mut self, open: bool) {
|
||||
@ -1089,6 +1118,7 @@ impl Hud {
|
||||
prompt_dialog: None,
|
||||
location_markers: MapMarkers::default(),
|
||||
salvage: false,
|
||||
trade_amount_input_key: None,
|
||||
},
|
||||
to_focus: None,
|
||||
//never_show: false,
|
||||
@ -2811,7 +2841,7 @@ impl Hud {
|
||||
}
|
||||
// Trade window
|
||||
if self.show.trade {
|
||||
match Trade::new(
|
||||
if let Some(action) = Trade::new(
|
||||
client,
|
||||
&self.imgs,
|
||||
&self.item_imgs,
|
||||
@ -2822,23 +2852,36 @@ impl Hud {
|
||||
i18n,
|
||||
&msm,
|
||||
self.pulse,
|
||||
&mut self.show,
|
||||
)
|
||||
.set(self.ids.trade, ui_widgets)
|
||||
{
|
||||
Some(action) => {
|
||||
if let TradeAction::Decline = action {
|
||||
self.show.stats = false;
|
||||
self.show.trade(false);
|
||||
if !self.show.social {
|
||||
self.show.want_grab = true;
|
||||
self.force_ungrab = false;
|
||||
} else {
|
||||
self.force_ungrab = true
|
||||
};
|
||||
}
|
||||
events.push(Event::TradeAction(action));
|
||||
},
|
||||
None => {},
|
||||
match action {
|
||||
Err(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();
|
||||
key.map(|k| {
|
||||
k.submit_action.map(|action| {
|
||||
self.events.push(Event::TradeAction(action));
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
Ok(action) => {
|
||||
if let TradeAction::Decline = action {
|
||||
self.show.stats = false;
|
||||
self.show.trade(false);
|
||||
if !self.show.social {
|
||||
self.show.want_grab = true;
|
||||
self.force_ungrab = false;
|
||||
} else {
|
||||
self.force_ungrab = true
|
||||
};
|
||||
}
|
||||
events.push(Event::TradeAction(action));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3859,15 +3902,15 @@ impl Hud {
|
||||
scale_mode
|
||||
}
|
||||
|
||||
// Checks if a TextEdit widget has the keyboard captured.
|
||||
fn typing(&self) -> bool {
|
||||
if let Some(id) = self.ui.widget_capturing_keyboard() {
|
||||
self.ui
|
||||
.widget_graph()
|
||||
/// Checks if a TextEdit widget has the keyboard captured.
|
||||
fn typing(&self) -> bool { Hud::is_captured::<widget::TextEdit>(&self.ui.ui) }
|
||||
|
||||
/// Checks if a widget of type `W` has captured the keyboard
|
||||
fn is_captured<W: Widget>(ui: &conrod_core::Ui) -> bool {
|
||||
if let Some(id) = ui.global_input().current.widget_capturing_keyboard {
|
||||
ui.widget_graph()
|
||||
.widget(id)
|
||||
.filter(|c| {
|
||||
c.type_id == std::any::TypeId::of::<<widget::TextEdit as Widget>::State>()
|
||||
})
|
||||
.filter(|c| c.type_id == std::any::TypeId::of::<<W as Widget>::State>())
|
||||
.is_some()
|
||||
} else {
|
||||
false
|
||||
|
@ -1,7 +1,7 @@
|
||||
use conrod_core::{
|
||||
color,
|
||||
position::Relative,
|
||||
widget::{self, Button, Image, Rectangle, State as ConrodState, Text},
|
||||
widget::{self, Button, Image, Rectangle, State as ConrodState, Text, TextEdit},
|
||||
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget, WidgetCommon,
|
||||
};
|
||||
use specs::Entity as EcsEntity;
|
||||
@ -30,10 +30,16 @@ use crate::{
|
||||
use super::{
|
||||
img_ids::{Imgs, ImgsRot},
|
||||
item_imgs::ItemImgs,
|
||||
slots::{SlotManager, TradeSlot},
|
||||
TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
|
||||
slots::{SlotKind, SlotManager, TradeSlot},
|
||||
Hud, Show, TradeAmountInput, TEXT_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum HudUpdate {
|
||||
Focus(widget::Id),
|
||||
Submit,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
ids: Ids,
|
||||
bg_ids: BackgroundIds,
|
||||
@ -55,6 +61,13 @@ widget_ids! {
|
||||
accept_button,
|
||||
decline_button,
|
||||
inventory_scroller,
|
||||
amount_bg,
|
||||
amount_notice,
|
||||
amount_open_label,
|
||||
amount_open_btn,
|
||||
amount_open_ovlay,
|
||||
amount_input,
|
||||
amount_btn,
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +85,7 @@ pub struct Trade<'a> {
|
||||
localized_strings: &'a Localization,
|
||||
msm: &'a MaterialStatManifest,
|
||||
pulse: f32,
|
||||
show: &'a mut Show,
|
||||
}
|
||||
|
||||
impl<'a> Trade<'a> {
|
||||
@ -86,6 +100,7 @@ impl<'a> Trade<'a> {
|
||||
localized_strings: &'a Localization,
|
||||
msm: &'a MaterialStatManifest,
|
||||
pulse: f32,
|
||||
show: &'a mut Show,
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
@ -99,6 +114,7 @@ impl<'a> Trade<'a> {
|
||||
localized_strings,
|
||||
msm,
|
||||
pulse,
|
||||
show,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,7 +177,7 @@ impl<'a> Trade<'a> {
|
||||
trade: &'a PendingTrade,
|
||||
prices: &'a Option<SitePrices>,
|
||||
ours: bool,
|
||||
) -> <Self as Widget>::Event {
|
||||
) -> Option<TradeAction> {
|
||||
let inventories = self.client.inventories();
|
||||
let check_if_us = |who: usize| -> Option<_> {
|
||||
let uid = trade.parties[who];
|
||||
@ -471,7 +487,7 @@ impl<'a> Trade<'a> {
|
||||
state: &mut ConrodState<'_, State>,
|
||||
ui: &mut UiCell<'_>,
|
||||
trade: &'a PendingTrade,
|
||||
) -> <Self as Widget>::Event {
|
||||
) -> Option<TradeAction> {
|
||||
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.
|
||||
@ -522,11 +538,139 @@ impl<'a> Trade<'a> {
|
||||
event
|
||||
}
|
||||
|
||||
fn input_item_amount(
|
||||
&mut self,
|
||||
state: &mut ConrodState<'_, State>,
|
||||
ui: &mut UiCell<'_>,
|
||||
trade: &'a PendingTrade,
|
||||
) -> Option<HudUpdate> {
|
||||
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| {
|
||||
let who: usize = trade.offers[0].get(&slot).and(Some(0)).unwrap_or(1);
|
||||
self.client
|
||||
.inventories()
|
||||
.get(t_s.entity)?
|
||||
.get(slot)
|
||||
.map(|item| (t_s.ours, slot, item.amount(), who))
|
||||
}),
|
||||
_ => None,
|
||||
});
|
||||
Rectangle::fill([132.0, 20.0])
|
||||
.bottom_right_with_margins_on(state.ids.bg_frame, 16.0, 32.0)
|
||||
.hsla(
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
if self.show.trade_amount_input_key.is_some() {
|
||||
0.75
|
||||
} else {
|
||||
0.35
|
||||
},
|
||||
)
|
||||
.set(state.ids.amount_bg, ui);
|
||||
if let Some((ours, slot, inv, who)) = selected {
|
||||
self.show.trade_amount_input_key = None;
|
||||
// Text for the amount of items offered.
|
||||
let input = trade.offers[who]
|
||||
.get(&slot)
|
||||
.map(|u| format!("{}", u))
|
||||
.unwrap_or_else(String::new);
|
||||
Text::new(&input)
|
||||
.top_left_with_margins_on(state.ids.amount_bg, 0.0, 22.0)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.color(TEXT_COLOR.alpha(0.7))
|
||||
.set(state.ids.amount_open_label, ui);
|
||||
if Button::image(self.imgs.edit_btn)
|
||||
.hover_image(self.imgs.edit_btn_hover)
|
||||
.press_image(self.imgs.edit_btn_press)
|
||||
.mid_left_with_margin_on(state.ids.amount_bg, 2.0)
|
||||
.w_h(16.0, 16.0)
|
||||
.set(state.ids.amount_open_btn, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
event = Some(HudUpdate::Focus(state.ids.amount_input));
|
||||
self.slot_manager.idle();
|
||||
self.show.trade_amount_input_key =
|
||||
Some(TradeAmountInput::new(slot, input, inv, ours, who));
|
||||
}
|
||||
Rectangle::fill_with([132.0, 20.0], color::TRANSPARENT)
|
||||
.top_left_of(state.ids.amount_bg)
|
||||
.graphics_for(state.ids.amount_open_btn)
|
||||
.set(state.ids.amount_open_ovlay, ui);
|
||||
} else if let Some(key) = &mut self.show.trade_amount_input_key {
|
||||
if !Hud::is_captured::<widget::TextEdit>(ui) && key.input_painted {
|
||||
// If the text edit is not captured submit the amount.
|
||||
event = Some(HudUpdate::Submit);
|
||||
}
|
||||
|
||||
if Button::image(self.imgs.close_btn)
|
||||
.hover_image(self.imgs.close_btn_hover)
|
||||
.press_image(self.imgs.close_btn_press)
|
||||
.mid_left_with_margin_on(state.ids.amount_bg, 2.0)
|
||||
.w_h(16.0, 16.0)
|
||||
.set(state.ids.amount_btn, ui)
|
||||
.was_clicked()
|
||||
{
|
||||
event = Some(HudUpdate::Submit);
|
||||
}
|
||||
// Input for making TradeAction requests
|
||||
key.input_painted = true;
|
||||
let text_color = key.err.as_ref().and(Some(color::RED)).unwrap_or(TEXT_COLOR);
|
||||
if let Some(new_input) = TextEdit::new(&key.input)
|
||||
.mid_left_with_margin_on(state.ids.amount_bg, 22.0)
|
||||
.w_h(138.0, 20.0)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.color(text_color)
|
||||
.set(state.ids.amount_input, ui)
|
||||
{
|
||||
if new_input != key.input {
|
||||
key.input = new_input.trim().to_owned();
|
||||
if !key.input.is_empty() {
|
||||
// trade amount can change with (shift||ctrl)-click
|
||||
let amount = *trade.offers[key.who].get(&key.slot).unwrap_or(&0);
|
||||
match key.input.parse::<i32>() {
|
||||
Ok(new_amount) => {
|
||||
key.input = format!("{}", new_amount);
|
||||
if new_amount > -1 && new_amount <= key.inv as i32 {
|
||||
key.err = None;
|
||||
let delta = new_amount - amount as i32;
|
||||
key.submit_action =
|
||||
TradeAction::item(key.slot, delta, key.ours);
|
||||
} else {
|
||||
key.err = Some("out of range".to_owned());
|
||||
key.submit_action = None;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
key.err = Some("bad quantity".to_owned());
|
||||
key.submit_action = None;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
key.submit_action = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// placeholder text when no trade slot is selected
|
||||
Text::new(self.localized_strings.get("hud.trade.amount_input"))
|
||||
.middle_of(state.ids.amount_bg)
|
||||
.font_id(self.fonts.cyri.conrod_id)
|
||||
.font_size(self.fonts.cyri.scale(14))
|
||||
.color(TEXT_GRAY_COLOR.alpha(0.25))
|
||||
.set(state.ids.amount_notice, ui);
|
||||
}
|
||||
event
|
||||
}
|
||||
|
||||
fn close_button(
|
||||
&mut self,
|
||||
state: &mut ConrodState<'_, State>,
|
||||
ui: &mut UiCell<'_>,
|
||||
) -> <Self as Widget>::Event {
|
||||
) -> Option<TradeAction> {
|
||||
if Button::image(self.imgs.close_btn)
|
||||
.w_h(24.0, 25.0)
|
||||
.hover_image(self.imgs.close_btn_hover)
|
||||
@ -543,7 +687,7 @@ impl<'a> Trade<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Widget for Trade<'a> {
|
||||
type Event = Option<TradeAction>;
|
||||
type Event = Option<Result<TradeAction, HudUpdate>>;
|
||||
type State = State;
|
||||
type Style = ();
|
||||
|
||||
@ -566,7 +710,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(TradeAction::Decline),
|
||||
None => return Some(Ok(TradeAction::Decline)),
|
||||
};
|
||||
|
||||
if state.ids.inv_alignment.len() < 2 {
|
||||
@ -595,7 +739,8 @@ 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);
|
||||
|
||||
event
|
||||
self.input_item_amount(state, ui, trade)
|
||||
.map(Err)
|
||||
.or_else(|| event.map(Ok))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user