From 6f22f31376d72aa327089ee4c5c436b2c39e12e1 Mon Sep 17 00:00:00 2001 From: jh0l Date: Sun, 6 Mar 2022 17:18:25 +1100 Subject: [PATCH 1/8] feat: :sparkles: a TextEdit input in the trade window so that users can specify the amount of an item to trade by typing --- common/src/trade.rs | 9 ++- voxygen/src/hud/img_ids.rs | 6 ++ voxygen/src/hud/mod.rs | 77 +++++++++++++----- voxygen/src/hud/trade.rs | 162 +++++++++++++++++++++++++++++++++++-- 4 files changed, 225 insertions(+), 29 deletions(-) diff --git a/common/src/trade.rs b/common/src/trade.rs index 42a807d1ff..bd9fa415f9 100644 --- a/common/src/trade.rs +++ b/common/src/trade.rs @@ -35,8 +35,13 @@ pub enum TradeAction { /// multiple times Accept(TradePhase), Decline, + Voxygen(VoxygenUpdate), +} +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum VoxygenUpdate { + Focus(usize), + Clear, } - #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum TradeResult { Completed, @@ -175,7 +180,7 @@ impl PendingTrade { self.accept_flags = [false, false]; } }, - Decline => {}, + _ => {}, } } } diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index b702d0bf14..dbd98ffd59 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -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", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 74f7caede8..caf5d74402 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -645,6 +645,32 @@ pub struct MapMarkers { group: HashMap>, } +/// (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, + who: usize, + input_painted: bool, +} + +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, + } + } +} + pub struct Show { ui: bool, intro: bool, @@ -676,6 +702,7 @@ pub struct Show { prompt_dialog: Option, location_markers: MapMarkers, salvage: bool, + trade_amount_input_key: Option, } impl Show { fn bag(&mut self, open: bool) { @@ -1089,6 +1116,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 +2839,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 +2850,30 @@ 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 => {}, + use common::trade::VoxygenUpdate::*; + match action { + TradeAction::Voxygen(update) => match update { + Focus(idx) => self.to_focus = Some(Some(widget::Id::new(idx))), + Clear => self.show.trade_amount_input_key = None, + }, + _ => { + 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,11 +3894,13 @@ 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::_typing(&self.ui.ui) } + + /// reusable function, avoids duplicating code + fn _typing(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::<::State>() diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index 36f428d4e7..2834995009 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -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; @@ -11,9 +11,9 @@ use client::Client; use common::{ comp::{ inventory::item::{ItemDesc, MaterialStatManifest, Quality}, - Inventory, Stats, + Inventory, Item, Stats, }, - trade::{PendingTrade, SitePrices, TradeAction, TradePhase}, + trade::{PendingTrade, SitePrices, TradeAction, TradePhase, VoxygenUpdate}, }; use common_net::sync::WorldSyncExt; use i18n::Localization; @@ -30,8 +30,8 @@ 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, }; pub struct State { @@ -55,6 +55,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 +79,7 @@ pub struct Trade<'a> { localized_strings: &'a Localization, msm: &'a MaterialStatManifest, pulse: f32, + show: &'a mut Show, } impl<'a> Trade<'a> { @@ -86,6 +94,7 @@ impl<'a> Trade<'a> { localized_strings: &'a Localization, msm: &'a MaterialStatManifest, pulse: f32, + show: &'a mut Show, ) -> Self { Self { client, @@ -99,6 +108,7 @@ impl<'a> Trade<'a> { localized_strings, msm, pulse, + show, } } } @@ -522,11 +532,149 @@ impl<'a> Trade<'a> { event } + fn input_item_amount( + &mut self, + state: &mut ConrodState<'_, State>, + ui: &mut UiCell<'_>, + trade: &'a PendingTrade, + ) -> ::Event { + use VoxygenUpdate::*; + 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) + .and_then(|item| Some((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 { + // Text for the amount of items offered. + let input = trade.offers[who] + .get(&slot) + .and_then(|u| Some(format!("{}", u))) + .unwrap_or(String::new()) + .clone(); + 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(TradeAction::Voxygen(Focus(state.ids.amount_input.index()))); + 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 selected.is_some() || (!Hud::_typing(&ui) && key.input_painted) { + event = Some(TradeAction::Voxygen(Clear)); + } + key.input_painted = true; + + 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(TradeAction::Voxygen(Clear)); + } + // Input for making TradeAction requests + 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).clone(); + match key.input.parse::() { + 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; + event = if delta > 0 { + Some(TradeAction::AddItem { + item: key.slot, + ours: key.ours, + quantity: delta as u32, + }) + } else if delta < 0 { + Some(TradeAction::RemoveItem { + item: key.slot, + ours: key.ours, + quantity: (delta * -1) as u32, + }) + } else { + None + } + } else { + key.err = Some("out of range".to_owned()); + } + }, + Err(_) => { + key.err = Some("bad quantity".to_owned()); + }, + } + } + } + } + } else { + // TODO i18n + // placeholder text when no trade slot is selected + Text::new("Select an item") + .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<'_>, - ) -> ::Event { + ) -> Option { if Button::image(self.imgs.close_btn) .w_h(24.0, 25.0) .hover_image(self.imgs.close_btn_hover) @@ -595,7 +743,7 @@ 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).or(event); event } } From 991cfcaa47fc1a2d7eb7a2ea00d6e8852f68a2e9 Mon Sep 17 00:00:00 2001 From: jh0l Date: Sun, 6 Mar 2022 17:21:58 +1100 Subject: [PATCH 2/8] feat: :globe_with_meridians: add i18n for trade amount input placeholder --- assets/voxygen/i18n/en/hud/trade.ron | 2 +- voxygen/src/hud/trade.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/assets/voxygen/i18n/en/hud/trade.ron b/assets/voxygen/i18n/en/hud/trade.ron index e65addffea..54dca95f59 100644 --- a/assets/voxygen/i18n/en/hud/trade.ron +++ b/assets/voxygen/i18n/en/hud/trade.ron @@ -23,10 +23,10 @@ "hud.trade.tooltip_hint_2": "", "hud.trade.your_offer": "Your offer", "hud.trade.their_offer": "Their offer", + "hud.trade.amount_input": "Select an item" }, vector_map: { } ) - diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index 2834995009..e9373fac7d 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -658,9 +658,8 @@ impl<'a> Trade<'a> { } } } else { - // TODO i18n // placeholder text when no trade slot is selected - Text::new("Select an item") + 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)) From de70ce61d8d52a58fa2de5e3056c1393c4948253 Mon Sep 17 00:00:00 2001 From: jh0l Date: Sun, 6 Mar 2022 17:18:25 +1100 Subject: [PATCH 3/8] feat: :sparkles: a TextEdit input in the trade window so that users can specify the amount of an item to trade by typing --- common/src/trade.rs | 20 ++++++++++++++++++++ voxygen/src/hud/trade.rs | 35 +++++++++++------------------------ 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/common/src/trade.rs b/common/src/trade.rs index bd9fa415f9..aa2ff8d193 100644 --- a/common/src/trade.rs +++ b/common/src/trade.rs @@ -1,3 +1,5 @@ +use std::{cmp::Ordering, sync::atomic::Ordering}; + use crate::{ comp::inventory::{slot::InvSlotId, trade_pricing::TradePricing, Inventory}, terrain::BiomeKind, @@ -99,6 +101,24 @@ impl TradePhase { } } +impl TradeAction { + pub fn delta_item(item: InvSlotId, delta: i32, ours: bool) -> Option { + 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 { diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index e9373fac7d..0a32eb526c 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + use conrod_core::{ color, position::Relative, @@ -11,7 +13,7 @@ use client::Client; use common::{ comp::{ inventory::item::{ItemDesc, MaterialStatManifest, Quality}, - Inventory, Item, Stats, + Inventory, Stats, }, trade::{PendingTrade, SitePrices, TradeAction, TradePhase, VoxygenUpdate}, }; @@ -547,7 +549,7 @@ impl<'a> Trade<'a> { .inventories() .get(t_s.entity)? .get(slot) - .and_then(|item| Some((t_s.ours, slot, item.amount(), who))) + .map(|item| (t_s.ours, slot, item.amount(), who)) }), _ => None, }); @@ -568,9 +570,8 @@ impl<'a> Trade<'a> { // Text for the amount of items offered. let input = trade.offers[who] .get(&slot) - .and_then(|u| Some(format!("{}", u))) - .unwrap_or(String::new()) - .clone(); + .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) @@ -595,7 +596,7 @@ impl<'a> Trade<'a> { .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 selected.is_some() || (!Hud::_typing(&ui) && key.input_painted) { + if selected.is_some() || (!Hud::_typing(ui) && key.input_painted) { event = Some(TradeAction::Voxygen(Clear)); } key.input_painted = true; @@ -624,28 +625,14 @@ impl<'a> Trade<'a> { 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).clone(); + let amount = trade.offers[key.who].get(&key.slot).unwrap_or(&0); match key.input.parse::() { 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; - event = if delta > 0 { - Some(TradeAction::AddItem { - item: key.slot, - ours: key.ours, - quantity: delta as u32, - }) - } else if delta < 0 { - Some(TradeAction::RemoveItem { - item: key.slot, - ours: key.ours, - quantity: (delta * -1) as u32, - }) - } else { - None - } + let delta = new_amount - *amount as i32; + event = TradeAction::delta_item(key.slot, delta, key.ours); } else { key.err = Some("out of range".to_owned()); } @@ -690,7 +677,7 @@ impl<'a> Trade<'a> { } impl<'a> Widget for Trade<'a> { - type Event = Option; + type Event = Result; type State = State; type Style = (); From 403a523ec7f1b9c69ec0784e4915e0c6618a9897 Mon Sep 17 00:00:00 2001 From: jh0l Date: Mon, 7 Mar 2022 09:39:01 +1100 Subject: [PATCH 4/8] refactor: :recycle: move voxygen hud update definitions from common/trade to voxygen/hud & trade --- common/src/trade.rs | 13 ++++--------- voxygen/src/hud/mod.rs | 9 ++++----- voxygen/src/hud/trade.rs | 35 ++++++++++++++++++++--------------- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/common/src/trade.rs b/common/src/trade.rs index aa2ff8d193..6326a13adb 100644 --- a/common/src/trade.rs +++ b/common/src/trade.rs @@ -1,4 +1,4 @@ -use std::{cmp::Ordering, sync::atomic::Ordering}; +use std::cmp::Ordering; use crate::{ comp::inventory::{slot::InvSlotId, trade_pricing::TradePricing, Inventory}, @@ -37,13 +37,8 @@ pub enum TradeAction { /// multiple times Accept(TradePhase), Decline, - Voxygen(VoxygenUpdate), -} -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum VoxygenUpdate { - Focus(usize), - Clear, } + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum TradeResult { Completed, @@ -102,13 +97,13 @@ impl TradePhase { } impl TradeAction { - pub fn delta_item(item: InvSlotId, delta: i32, ours: bool) -> Option { + pub fn item(item: InvSlotId, delta: i32, ours: bool) -> Option { match delta.cmp(&0) { Ordering::Equal => None, Ordering::Less => Some(TradeAction::RemoveItem { item, ours, - quantity: delta as u32, + quantity: -delta as u32, }), Ordering::Greater => Some(TradeAction::AddItem { item, diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index caf5d74402..60dfb92825 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -2854,13 +2854,12 @@ impl Hud { ) .set(self.ids.trade, ui_widgets) { - use common::trade::VoxygenUpdate::*; match action { - TradeAction::Voxygen(update) => match update { - Focus(idx) => self.to_focus = Some(Some(widget::Id::new(idx))), - Clear => self.show.trade_amount_input_key = None, + Err(update) => match update { + trade::HudUpdate::Focus(idx) => self.to_focus = Some(Some(idx)), + trade::HudUpdate::Clear => self.show.trade_amount_input_key = None, }, - _ => { + Ok(action) => { if let TradeAction::Decline = action { self.show.stats = false; self.show.trade(false); diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index 0a32eb526c..d862054329 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -1,5 +1,3 @@ -use std::cmp::Ordering; - use conrod_core::{ color, position::Relative, @@ -15,7 +13,7 @@ use common::{ inventory::item::{ItemDesc, MaterialStatManifest, Quality}, Inventory, Stats, }, - trade::{PendingTrade, SitePrices, TradeAction, TradePhase, VoxygenUpdate}, + trade::{PendingTrade, SitePrices, TradeAction, TradePhase}, }; use common_net::sync::WorldSyncExt; use i18n::Localization; @@ -36,6 +34,12 @@ use super::{ Hud, Show, TradeAmountInput, TEXT_COLOR, TEXT_GRAY_COLOR, UI_HIGHLIGHT_0, UI_MAIN, }; +#[derive(Debug)] +pub enum HudUpdate { + Focus(widget::Id), + Clear, +} + pub struct State { ids: Ids, bg_ids: BackgroundIds, @@ -173,7 +177,7 @@ impl<'a> Trade<'a> { trade: &'a PendingTrade, prices: &'a Option, ours: bool, - ) -> ::Event { + ) -> Option { let inventories = self.client.inventories(); let check_if_us = |who: usize| -> Option<_> { let uid = trade.parties[who]; @@ -483,7 +487,7 @@ impl<'a> Trade<'a> { state: &mut ConrodState<'_, State>, ui: &mut UiCell<'_>, trade: &'a PendingTrade, - ) -> ::Event { + ) -> Option { 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. @@ -540,7 +544,6 @@ impl<'a> Trade<'a> { ui: &mut UiCell<'_>, trade: &'a PendingTrade, ) -> ::Event { - use VoxygenUpdate::*; 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| { @@ -586,7 +589,7 @@ impl<'a> Trade<'a> { .set(state.ids.amount_open_btn, ui) .was_clicked() { - event = Some(TradeAction::Voxygen(Focus(state.ids.amount_input.index()))); + event = Some(Err(HudUpdate::Focus(state.ids.amount_input))); self.slot_manager.idle(); self.show.trade_amount_input_key = Some(TradeAmountInput::new(slot, input, inv, ours, who)); @@ -597,7 +600,7 @@ impl<'a> Trade<'a> { .set(state.ids.amount_open_ovlay, ui); } else if let Some(key) = &mut self.show.trade_amount_input_key { if selected.is_some() || (!Hud::_typing(ui) && key.input_painted) { - event = Some(TradeAction::Voxygen(Clear)); + event = Some(Err(HudUpdate::Clear)); } key.input_painted = true; @@ -609,7 +612,7 @@ impl<'a> Trade<'a> { .set(state.ids.amount_btn, ui) .was_clicked() { - event = Some(TradeAction::Voxygen(Clear)); + event = Some(Err(HudUpdate::Clear)); } // Input for making TradeAction requests let text_color = key.err.as_ref().and(Some(color::RED)).unwrap_or(TEXT_COLOR); @@ -625,14 +628,14 @@ impl<'a> Trade<'a> { 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); + let amount = *trade.offers[key.who].get(&key.slot).unwrap_or(&0); match key.input.parse::() { 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; - event = TradeAction::delta_item(key.slot, delta, key.ours); + let delta = new_amount - amount as i32; + event = TradeAction::item(key.slot, delta, key.ours).map(Ok); } else { key.err = Some("out of range".to_owned()); } @@ -677,7 +680,7 @@ impl<'a> Trade<'a> { } impl<'a> Widget for Trade<'a> { - type Event = Result; + type Event = Option>; type State = State; type Style = (); @@ -700,7 +703,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 { @@ -729,7 +732,9 @@ 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).or(event); + let event = self + .input_item_amount(state, ui, trade) + .or_else(|| event.map(Ok)); event } } From 8194c19647224fe9c5fe3b8be5796064bbdfa9d6 Mon Sep 17 00:00:00 2001 From: Jack hollis-london Date: Sun, 6 Mar 2022 23:43:03 +0000 Subject: [PATCH 5/8] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfefd263c9..3c59e895dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 From aeae94d31b85d2a07143ce2a6f31e68923b51902 Mon Sep 17 00:00:00 2001 From: Jack hollis-london Date: Mon, 7 Mar 2022 00:30:32 +0000 Subject: [PATCH 6/8] small refactor per code review --- common/src/trade.rs | 2 +- voxygen/src/hud/mod.rs | 13 ++++++------- voxygen/src/hud/trade.rs | 6 +++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/common/src/trade.rs b/common/src/trade.rs index 6326a13adb..f568f285c1 100644 --- a/common/src/trade.rs +++ b/common/src/trade.rs @@ -195,7 +195,7 @@ impl PendingTrade { self.accept_flags = [false, false]; } }, - _ => {}, + Decline => {}, } } } diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 60dfb92825..dfcea44cb0 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3893,17 +3893,16 @@ impl Hud { scale_mode } - /// Checks if a TextEdit widget has the keyboard captured. - fn typing(&self) -> bool { Hud::_typing(&self.ui.ui) } - /// reusable function, avoids duplicating code - fn _typing(ui: &conrod_core::Ui) -> bool { + /// Checks if a TextEdit widget has the keyboard captured. + fn typing(&self) -> bool { Hud::is_captured::(&self.ui.ui) } + + /// Checks if a widget of type `W` has captured the keyboard + fn is_captured(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::<::State>() - }) + .filter(|c| c.type_id == std::any::TypeId::of::<::State>()) .is_some() } else { false diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index d862054329..7e6a6a550b 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -599,7 +599,11 @@ impl<'a> Trade<'a> { .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 selected.is_some() || (!Hud::_typing(ui) && key.input_painted) { + if selected.is_some() + || (!Hud::is_captured::(ui) && key.input_painted) + { + // If the user has selected an item, or if the text edit is not captured, then + // we can close the text edit. event = Some(Err(HudUpdate::Clear)); } key.input_painted = true; From 32d4ddd4fd7dd3ab47e2e3174a3d414685b7b139 Mon Sep 17 00:00:00 2001 From: jh0l Date: Tue, 8 Mar 2022 05:48:50 +1100 Subject: [PATCH 7/8] style: :art: remove newline --- voxygen/src/hud/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index dfcea44cb0..a4bcac4723 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3893,7 +3893,6 @@ impl Hud { scale_mode } - /// Checks if a TextEdit widget has the keyboard captured. fn typing(&self) -> bool { Hud::is_captured::(&self.ui.ui) } From 585bcda7293860d395b8e284e9666dc292b59ace Mon Sep 17 00:00:00 2001 From: jh0l Date: Tue, 8 Mar 2022 18:35:09 +1100 Subject: [PATCH 8/8] fix: :bug: resolve issue where client spams TradeAction updates based on out of sync PendingTrade values --- voxygen/src/hud/mod.rs | 11 ++++++++++- voxygen/src/hud/trade.rs | 34 ++++++++++++++++++---------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a4bcac4723..aa5718fe16 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -655,6 +655,7 @@ pub struct TradeAmountInput { err: Option, who: usize, input_painted: bool, + submit_action: Option, } impl TradeAmountInput { @@ -667,6 +668,7 @@ impl TradeAmountInput { who, err: None, input_painted: false, + submit_action: None, } } } @@ -2857,7 +2859,14 @@ impl Hud { match action { Err(update) => match update { trade::HudUpdate::Focus(idx) => self.to_focus = Some(Some(idx)), - trade::HudUpdate::Clear => self.show.trade_amount_input_key = None, + 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 { diff --git a/voxygen/src/hud/trade.rs b/voxygen/src/hud/trade.rs index 7e6a6a550b..7280fe7cfc 100644 --- a/voxygen/src/hud/trade.rs +++ b/voxygen/src/hud/trade.rs @@ -37,7 +37,7 @@ use super::{ #[derive(Debug)] pub enum HudUpdate { Focus(widget::Id), - Clear, + Submit, } pub struct State { @@ -543,7 +543,7 @@ impl<'a> Trade<'a> { state: &mut ConrodState<'_, State>, ui: &mut UiCell<'_>, trade: &'a PendingTrade, - ) -> ::Event { + ) -> Option { 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| { @@ -570,6 +570,7 @@ impl<'a> Trade<'a> { ) .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) @@ -589,7 +590,7 @@ impl<'a> Trade<'a> { .set(state.ids.amount_open_btn, ui) .was_clicked() { - event = Some(Err(HudUpdate::Focus(state.ids.amount_input))); + 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)); @@ -599,14 +600,10 @@ impl<'a> Trade<'a> { .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 selected.is_some() - || (!Hud::is_captured::(ui) && key.input_painted) - { - // If the user has selected an item, or if the text edit is not captured, then - // we can close the text edit. - event = Some(Err(HudUpdate::Clear)); + if !Hud::is_captured::(ui) && key.input_painted { + // If the text edit is not captured submit the amount. + event = Some(HudUpdate::Submit); } - key.input_painted = true; if Button::image(self.imgs.close_btn) .hover_image(self.imgs.close_btn_hover) @@ -616,9 +613,10 @@ impl<'a> Trade<'a> { .set(state.ids.amount_btn, ui) .was_clicked() { - event = Some(Err(HudUpdate::Clear)); + 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) @@ -639,15 +637,20 @@ impl<'a> Trade<'a> { if new_amount > -1 && new_amount <= key.inv as i32 { key.err = None; let delta = new_amount - amount as i32; - event = TradeAction::item(key.slot, delta, key.ours).map(Ok); + 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; } } } @@ -736,9 +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); - let event = self - .input_item_amount(state, ui, trade) - .or_else(|| event.map(Ok)); - event + self.input_item_amount(state, ui, trade) + .map(Err) + .or_else(|| event.map(Ok)) } }