From 1b3536a5f567aed146bd4bf76c9a5d3daaf947c9 Mon Sep 17 00:00:00 2001 From: hqurve Date: Wed, 7 Apr 2021 20:24:22 +0000 Subject: [PATCH] Added search to crafting and social windows, added i18n support to crafting tabs, fixed social window offset when group open, removed tabs in social window --- CHANGELOG.md | 2 + assets/voxygen/element/buttons/search_btn.png | Bin 0 -> 151 bytes .../element/buttons/search_btn_hover.png | Bin 0 -> 1725 bytes .../element/buttons/search_btn_press.png | Bin 0 -> 1705 bytes assets/voxygen/element/buttons/social_tab.png | Bin 146 -> 0 bytes .../element/buttons/social_tab_active.png | Bin 470 -> 0 bytes .../element/buttons/social_tab_hover.png | Bin 190 -> 0 bytes .../element/buttons/social_tab_inactive.png | Bin 477 -> 0 bytes .../element/buttons/social_tab_press.png | Bin 161 -> 0 bytes .../element/buttons/social_tab_pressed.png | Bin 128 -> 0 bytes assets/voxygen/element/icons/globe.png | Bin 0 -> 278 bytes assets/voxygen/element/icons/globe_2.png | Bin 0 -> 273 bytes assets/voxygen/element/misc_bg/crafting.png | Bin 7737 -> 7805 bytes .../element/misc_bg/crafting_frame.png | Bin 15174 -> 8189 bytes assets/voxygen/element/misc_bg/social_bg.png | Bin 6109 -> 5842 bytes .../voxygen/element/misc_bg/social_frame.png | Bin 6579 -> 4768 bytes assets/voxygen/i18n/en/hud/crafting.ron | 12 + voxygen/src/hud/crafting.rs | 498 ++++++++----- voxygen/src/hud/img_ids.rs | 14 +- voxygen/src/hud/mod.rs | 56 +- voxygen/src/hud/social.rs | 686 +++++++----------- 21 files changed, 641 insertions(+), 627 deletions(-) create mode 100644 assets/voxygen/element/buttons/search_btn.png create mode 100644 assets/voxygen/element/buttons/search_btn_hover.png create mode 100644 assets/voxygen/element/buttons/search_btn_press.png delete mode 100644 assets/voxygen/element/buttons/social_tab.png delete mode 100644 assets/voxygen/element/buttons/social_tab_active.png delete mode 100644 assets/voxygen/element/buttons/social_tab_hover.png delete mode 100644 assets/voxygen/element/buttons/social_tab_inactive.png delete mode 100644 assets/voxygen/element/buttons/social_tab_press.png delete mode 100644 assets/voxygen/element/buttons/social_tab_pressed.png create mode 100644 assets/voxygen/element/icons/globe.png create mode 100644 assets/voxygen/element/icons/globe_2.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 50b0d3fd0a..0e5340d0e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Crafting menu tabs - Auto camera setting, making the game easier to play with one hand - Topographic map option +- Search bars for social and crafting window ### Changed @@ -48,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added a raycast check to beams to prevent their effect applying through walls - Flying agents raycast more angles to check for obstacles. - Mouse Cursor now locks to the center of the screen when menu is not open +- Social window no longer moves when group is open ## [0.9.0] - 2021-03-20 diff --git a/assets/voxygen/element/buttons/search_btn.png b/assets/voxygen/element/buttons/search_btn.png new file mode 100644 index 0000000000000000000000000000000000000000..2c17a667909870c9630d33d566b933f24f720959 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a)4I8PVH5Dn4fgau*|JByyyr}SKp znQ-XLnl=9oTouebc#a)kkT~i8LWU&_YHbT8B88U7Eo)f7wrK7Z`Lw_GXD3C0|m9C$kdXcL2{tDnm{r-UW|5Ro+n literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/buttons/search_btn_hover.png b/assets/voxygen/element/buttons/search_btn_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..ec0227192759e753f1e389734dae63dbc60ada47 GIT binary patch literal 1725 zcmbVNO^n+_6!uoMqRXz3Xb*)0vb>j~;&^P2o!F|Ww#m{&YSLvJ=&sxR=)C=mV%X9wNMd^>Q?07u$&G)|d zGcWHgH_pt@J~t}}!hEe-S%LRl{ycdIeqZap@jJW@$JMn|5S}`cKQqF$Pmc-0!Mj0o zHCwHpwJ8q^#N#e2^urj?f^hOwKPGg8WunVkLF7n(efPa22A(5bFzTotmsvZgZYS*g zcB4tRH>l-Fr(P0I_H7^tSw_Tu*ojiR???k)8^-yxB8dY?w&6&Nxj}KYzATn`!bG!x zWQve37OjG6VnZvwC~8PUiVC+Ot3?}^Y+Vyamju=l&$m}9FOO`&mm{^aEVdP;*XtE} zdVwb`MYSwTL7JjzG9cu1E6PY;j?#rOLxrU@3F0i^k(e_Qmv=Kq0;NM2!gxX(rK2!G zz?43T6}5nJmj*zOPH=HI=?si}RAC(!vM5Udt4^?Sn`b<2^9N8*miHNe(AMh{8&kD} z;lxCmEp37uqkv3Br_HUHDJv}H-Gs8GO^~^edlTE`gprIVP0l;xL@kdci<(wY#p4%( z$m6~Ag*_Wsg=EZ;AZx0Ou#C*6hHRwU20no_8=)bz&b`3jng-QP8yWkcP%<9L$RoiX zwSAt11RM@R(qc*+wIp#8M!U>AJORU?PTxCUE0>oO?gt%MNLS9B7HdmoQ?*P}1|J7< z>vg*pr5TARt5qBcf>Q_r&$cktJd0qtRANOLF>J_|iok8n)G;Q8kBi0e{tBnvyae+7 z!_(k#3LO8+^GXC0WT>)BQYO<*VyI-Q1S>~ASToS;6B%QsixhfABUDMYr z-Oyznl}sSRL?$j~GIo7~F_#itqIua5D#@+}Ddc!-bdb)ow??B*AP$PhCNw`3jzsfn zV4gIZ4yMH8u`G}F_u33dA5tYl>nZoM9!c0@3u5=kDpVdMJ|&wEm;QLE9BP^-eP7l7 zAL*m8&^C!$3_7eL<<2O1FB&FA`ER}kYkT_E7!J)aA5OY3d`$W{i-08b5xrwzhsyoO5^ZZ@&oL1II4@ zaQIE(o81NL+Vw*p>g#UlvnQVUJGXy&e`gNgx^(n;;oedGI{o_UyRUxm n#mrSV6K>zQ_-Ete;0_XQ9{Baj{3R;pe=xPBM&+y1>u>)9%mpEw literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/buttons/search_btn_press.png b/assets/voxygen/element/buttons/search_btn_press.png new file mode 100644 index 0000000000000000000000000000000000000000..534829a74cb841d460130a9995f66574c1e0478b GIT binary patch literal 1705 zcmbVNON`q@81@P+l$O1KR$A@JxR*sR_V^XsqlWD^*>)q5CE5nMNLAIzc#>EfdtBSu zBzr;aVFeNrNT`TMFFb_AWjP}b2xxCTRYKz0UN~?-hzqBd-+8f%(uc6@cs%pX_kaIm z{=8e;JhyOs`M4xW3zd!X7QCO!pJPwM@0;C=zrpL-c;l^9l1{vkKXcNjHJuOcc}C@a*ojioFDgS`0^@vIQ{*8e+bJq*xj}imT9Zp6;c`L6 zh+%BXj;HDc%Ql=>WCI&m)8V#}?hwl*rXi0n1*|20lWdjGj%~qLQE6vcOf;?6>#03c z6-i6eJ!pJ36$%J^9LlX$ zNhL}%8Zlle7ZnJO8U#LRHce(b1@N=k@Q`C#98t4jAcr&N7}Ud_;ZOFL1?%P|knbOz z2467X_*b6gdRXTL3%S_!kPX$38lGt*%fby?*O|p!cf$6162O6^ox`lTDjyhq?or)t zI>>Q7ifq@?QN!gN`HXYNaT-kbXkPZiN+KIU3OOE(57K#lWjyKx^00UaW%;2fDlD%C z4*ra$gBkI7BFp3by*3BZM^wqkOe&gLk0yMr1+jZ<6>1L?pVHk&OMkLdjx^1Zexz#u zkMwa^Ser&I4jooga%Z%>7mbpl{Wo93wL^Vt0*7Xp52sxiKBj$~M?jrGm;U9&FK$AI zzf>u&)mIkT-*3Nnb?%w_@9+P3&t3fX;&;!_|7_i@-}vPEyt8<7KltgJx!e15@@M^p z+Ldd|?{x1xxQ~B-5PY;>9xNZYw@?4I`uXKsUoY)^@ZncW2UoXmt!yf1^~ed6k~CayA#8@b22Z193M{?#}JF& zWRaBgf9qGSJ?7wF@aaw|o13=s+5h7Cc8gXY{q?Gd$ERegAZJgMP1eU1N3Sp!CGGhy uDIxKq>*(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ-TuDShRCwC#Slw-gFbq6fjDUpb1h51{AckNhmVj|!gZ6

^}4}3L&(#<)`Y$ zy#P_*d^Qf`NL2v9Zj5N*_$_%o}zdy1E&*DeUFRqJjB*k zrxROSJy(s1=$aZI@{zb}{HKVS`5IyoxoiB77ZB^^JJolpZ{nNy$2ffNl;?RGavb=~ z-aS;YDfH2G-8BoyEX~Y_0K7aRRlg2fepA=R+P0l-S;lds)wlXPUR7wWuWHRT0sykf zNs{R7pXgzn__mX8Be^PF>&aL6WgnY&6(UvDX=ClD*bTt-yVmeN08N|Hx*p#yEdT%j M07*qoM6N<$g3n09z5oCK diff --git a/assets/voxygen/element/buttons/social_tab_hover.png b/assets/voxygen/element/buttons/social_tab_hover.png deleted file mode 100644 index 37f2be62bd639d83d0e5525811cd957a6e77538a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 190 zcmeAS@N?(olHy`uVBq!ia0vp^azM<(!3HEt^>~ed6k~CayA#8@b22Z1oC;4D#}JF& zWRaBgf9qGSJ?7wF@aaw|o13=s+5h7Cc8gXY{q?GdN5`OV+cvXUoxN3GwU~k6P?E%t zzdw0>byA*+IzBYMcsM90&G=z}d$GYGT{nM*D=aV4l9yyfMfEW~Ia2Z8*uda|=;_Xn m|I!UjE+iT(>6Z^=U|8;L{QJm>y-7efFnGH9xvX(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ-V@X6oRCwC#Sle-gFbrI2=z*bwb2|(@KuI(K?YM+|(1VnZM2;~gAqkI6j{V`z z1Gm~;#9@)D0sy+Md;72fL<9h^Yi%w5-uFFy_8SF$iChtzu4~&?0bm@*tO8~Z>#7PO z0uez(z|0U400`&Rb-gG|RTmBvY2^kZ$-&4(WKl>;xaLGq?7e@%4)ESrMl2#|nkI;d zkBprWD>eJ$8_$l#-o%f`W9Hm23{@PZ-2=NTPGPyHSr~ed6k~CayA#8@b22Z1oM=xM#}JF& zWRaBgf9qGSJ?7wF@b{H2o0xB|XFPin@Ihd^r>JYD@< J);T3K0RRlGHlzRm diff --git a/assets/voxygen/element/buttons/social_tab_pressed.png b/assets/voxygen/element/buttons/social_tab_pressed.png deleted file mode 100644 index e2edd9115b692f6a4871f76b3a1de674b6767607..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmeAS@N?(olHy`uVBq!ia0vp^azM<(!3HEt^>~ed6k~CayA#8@b22Z194k*3#}JF& zWRaBgfBEV8#~l0%UOdobbE`K${r|asOyY!z6Bo`tx=4wM$ERd7FKsURARBa(9Kkwm&=Ll7wXz+J1A58669F6zTxMYmx51E34oK)BEK?n>BJ cH}H#vch^rWK;nq$0ssI207*qoM6N<$f}Ngr)c^nh literal 0 HcmV?d00001 diff --git a/assets/voxygen/element/icons/globe_2.png b/assets/voxygen/element/icons/globe_2.png new file mode 100644 index 0000000000000000000000000000000000000000..67b4ef82d089d3b9680a469c3b0c75ca4006f875 GIT binary patch literal 273 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 MkJ}w9If_mC4;1s+PXGV_ literal 7737 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 delta 5093 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 literal 6579 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, } #[derive(WidgetCommon)] @@ -120,7 +128,8 @@ impl<'a> Crafting<'a> { } #[derive(Debug, EnumIter, PartialEq)] -pub enum SelectedCraftingTab { +pub enum CraftingTab { + All, Armor, Weapon, Food, @@ -131,6 +140,61 @@ pub enum SelectedCraftingTab { Utility, Glider, } +impl CraftingTab { + fn name_key(&self) -> &str { + match self { + CraftingTab::All => "hud.crafting.tabs.all", + CraftingTab::Armor => "hud.crafting.tabs.armor", + CraftingTab::Dismantle => "hud.crafting.tabs.dismantle", + CraftingTab::Food => "hud.crafting.tabs.food", + CraftingTab::Glider => "hud.crafting.tabs.glider", + CraftingTab::Potion => "hud.crafting.tabs.potion", + CraftingTab::Tool => "hud.crafting.tabs.tool", + CraftingTab::Utility => "hud.crafting.tabs.utility", + CraftingTab::Weapon => "hud.crafting.tabs.weapon", + CraftingTab::Bag => "hud.crafting.tabs.bag", + } + } + + fn img_id(&self, imgs: &Imgs) -> image::Id { + match self { + CraftingTab::All => imgs.icon_globe, + CraftingTab::Armor => imgs.icon_armor, + CraftingTab::Dismantle => imgs.icon_dismantle, + CraftingTab::Food => imgs.icon_food, + CraftingTab::Glider => imgs.icon_glider, + CraftingTab::Potion => imgs.icon_potion, + CraftingTab::Tool => imgs.icon_tools, + CraftingTab::Utility => imgs.icon_utility, + CraftingTab::Weapon => imgs.icon_weapon, + CraftingTab::Bag => imgs.icon_bag, + } + } + + fn satisfies(&self, item: &ItemDef) -> bool { + match self { + CraftingTab::All => true, + CraftingTab::Food => item.tags().contains(&ItemTag::Food), + CraftingTab::Armor => match item.kind() { + ItemKind::Armor(_) => !item.tags().contains(&ItemTag::Bag), + _ => false, + }, + CraftingTab::Glider => matches!(item.kind(), ItemKind::Glider(_)), + CraftingTab::Potion => item.tags().contains(&ItemTag::Potion), + CraftingTab::Bag => item.tags().contains(&ItemTag::Bag), + CraftingTab::Tool => item.tags().contains(&ItemTag::CraftingTool), + CraftingTab::Utility => item.tags().contains(&ItemTag::Utility), + CraftingTab::Weapon => match item.kind() { + ItemKind::Tool(_) => !item.tags().contains(&ItemTag::CraftingTool), + _ => false, + }, + CraftingTab::Dismantle => match item.kind() { + ItemKind::Ingredient { .. } => !item.tags().contains(&ItemTag::CraftingTool), + _ => false, + }, + } + } +} pub struct State { ids: Ids, @@ -155,15 +219,6 @@ impl<'a> Widget for Crafting<'a> { fn update(self, args: widget::UpdateArgs) -> Self::Event { let widget::UpdateArgs { state, ui, .. } = args; - if state.ids.recipe_names.len() < self.client.recipe_book().iter().len() { - state.update(|state| { - state.ids.recipe_names.resize( - self.client.recipe_book().iter().len(), - &mut ui.widget_id_generator(), - ) - }); - } - let mut events = Vec::new(); // Tooltips @@ -211,21 +266,36 @@ impl<'a> Widget for Crafting<'a> { .font_id(self.fonts.cyri.conrod_id) .desc_text_color(TEXT_COLOR); + // Frame and window Image::new(self.imgs.crafting_window) .bottom_right_with_margins_on(ui.window, 308.0, 450.0) .color(Some(UI_MAIN)) .w_h(422.0, 460.0) .set(state.ids.window, ui); + // Search Background + // I couldn't find a way to manually set they layer of a widget + // If it is possible, please move this code (for rectangle) down to the code for + // search input + if self.show.crafting_search_key.is_some() { + Rectangle::fill([114.0, 20.0]) + .top_left_with_margins_on(state.ids.window, 52.0, 26.0) + .hsla(0.0, 0.0, 0.0, 0.7) + .set(state.ids.input_bg_search, ui); + } + // Window Image::new(self.imgs.crafting_frame) .middle_of(state.ids.window) .color(Some(UI_HIGHLIGHT_0)) .w_h(422.0, 460.0) .set(state.ids.window_frame, ui); + + // Crafting Icon Image::new(self.imgs.crafting_icon_bordered) .w_h(38.0, 38.0) .top_left_with_margins_on(state.ids.window_frame, 4.0, 4.0) .set(state.ids.icon, ui); - // Close Button + + // Close Button if Button::image(self.imgs.close_button) .w_h(24.0, 25.0) .hover_image(self.imgs.close_button_hover) @@ -254,105 +324,83 @@ impl<'a> Widget for Crafting<'a> { .top_right_with_margins_on(state.ids.window, 74.0, 5.0) .scroll_kids_vertically() .set(state.ids.align_ing, ui); + // Category Tabs - if state.ids.category_bgs.len() < SelectedCraftingTab::iter().enumerate().len() { + if state.ids.category_bgs.len() < CraftingTab::iter().enumerate().len() { state.update(|s| { s.ids.category_bgs.resize( - SelectedCraftingTab::iter().enumerate().len(), + CraftingTab::iter().enumerate().len(), &mut ui.widget_id_generator(), ) }) }; - if state.ids.category_tabs.len() < SelectedCraftingTab::iter().enumerate().len() { + if state.ids.category_tabs.len() < CraftingTab::iter().enumerate().len() { state.update(|s| { s.ids.category_tabs.resize( - SelectedCraftingTab::iter().enumerate().len(), + CraftingTab::iter().enumerate().len(), &mut ui.widget_id_generator(), ) }) }; - if state.ids.category_imgs.len() < SelectedCraftingTab::iter().enumerate().len() { + if state.ids.category_imgs.len() < CraftingTab::iter().enumerate().len() { state.update(|s| { s.ids.category_imgs.resize( - SelectedCraftingTab::iter().enumerate().len(), + CraftingTab::iter().enumerate().len(), &mut ui.widget_id_generator(), ) }) }; let sel_crafting_tab = &self.show.crafting_tab; - for i in SelectedCraftingTab::iter().enumerate() { - // TODO: i18n! - let tab_name = match i.1 { - SelectedCraftingTab::Armor => "Armor", - SelectedCraftingTab::Dismantle => "Dismantle", - SelectedCraftingTab::Food => "Food", - SelectedCraftingTab::Glider => "Gliders", - SelectedCraftingTab::Potion => "Potions", - SelectedCraftingTab::Tool => "Tools", - SelectedCraftingTab::Utility => "Utility", - SelectedCraftingTab::Weapon => "Weapons", - SelectedCraftingTab::Bag => "Bags", - }; - let tab_img = match i.1 { - SelectedCraftingTab::Armor => self.imgs.icon_armor, - SelectedCraftingTab::Dismantle => self.imgs.icon_dismantle, - SelectedCraftingTab::Food => self.imgs.icon_food, - SelectedCraftingTab::Glider => self.imgs.icon_glider, - SelectedCraftingTab::Potion => self.imgs.icon_potion, - SelectedCraftingTab::Tool => self.imgs.icon_tools, - SelectedCraftingTab::Utility => self.imgs.icon_utility, - SelectedCraftingTab::Weapon => self.imgs.icon_weapon, - SelectedCraftingTab::Bag => self.imgs.icon_bag, - }; + for (i, crafting_tab) in CraftingTab::iter().enumerate() { + let tab_img = crafting_tab.img_id(self.imgs); // Button Background let mut bg = Image::new(self.imgs.pixel) .w_h(40.0, 30.0) .color(Some(UI_MAIN)); - if i.0 == 0 { + if i == 0 { bg = bg.top_left_with_margins_on(state.ids.window_frame, 50.0, -40.0) } else { - bg = bg.down_from(state.ids.category_bgs[i.0 - 1], 0.0) + bg = bg.down_from(state.ids.category_bgs[i - 1], 0.0) }; - bg.set(state.ids.category_bgs[i.0], ui); + bg.set(state.ids.category_bgs[i], ui); // Category Button - if Button::image(if i.1 == *sel_crafting_tab { + if Button::image(if crafting_tab == *sel_crafting_tab { self.imgs.wpn_icon_border_pressed } else { self.imgs.wpn_icon_border }) - .wh_of(state.ids.category_bgs[i.0]) - .middle_of(state.ids.category_bgs[i.0]) - .hover_image(if i.1 == *sel_crafting_tab { + .wh_of(state.ids.category_bgs[i]) + .middle_of(state.ids.category_bgs[i]) + .hover_image(if crafting_tab == *sel_crafting_tab { self.imgs.wpn_icon_border_pressed } else { self.imgs.wpn_icon_border_mo }) - .press_image(if i.1 == *sel_crafting_tab { + .press_image(if crafting_tab == *sel_crafting_tab { self.imgs.wpn_icon_border_pressed } else { self.imgs.wpn_icon_border_press }) .with_tooltip( self.tooltip_manager, - tab_name, + &self.localized_strings.get(crafting_tab.name_key()), "", &tabs_tooltip, TEXT_COLOR, ) - .set(state.ids.category_tabs[i.0], ui) + .set(state.ids.category_tabs[i], ui) .was_clicked() { - events.push(Event::ChangeCraftingTab(i.1)) + events.push(Event::ChangeCraftingTab(crafting_tab)) }; // Tab images Image::new(tab_img) - .middle_of(state.ids.category_tabs[i.0]) + .middle_of(state.ids.category_tabs[i]) .w_h(20.0, 20.0) - .graphics_for(state.ids.category_tabs[i.0]) - .set(state.ids.category_imgs[i.0], ui); + .graphics_for(state.ids.category_tabs[i]) + .set(state.ids.category_imgs[i], ui); } - let client = &self.client; // First available recipes, then unavailable ones, each alphabetically // In the triples, "name" is the recipe book key, and "recipe.output.0.name()" // is the display name (as stored in the item descriptors) @@ -366,6 +414,17 @@ impl<'a> Widget for Crafting<'a> { .client .recipe_book() .iter() + .filter(|(_, recipe)| { + let output_name = recipe.output.0.name.to_lowercase(); + if let Some(key) = &self.show.crafting_search_key { + key.as_str() + .to_lowercase() + .split_whitespace() + .all(|substring| output_name.contains(substring)) + } else { + true + } + }) .map(|(name, recipe)| { let at_least_some_ingredients = recipe.inputs.iter().any(|(input, amount)| { *amount > 0 @@ -375,7 +434,7 @@ impl<'a> Widget for Crafting<'a> { .unwrap_or(false) }) }); - let state = if client.available_recipes().contains(name.as_str()) { + let state = if self.client.available_recipes().contains(name.as_str()) { RecipeIngredientQuantity::All } else if at_least_some_ingredients { RecipeIngredientQuantity::Some @@ -386,129 +445,19 @@ impl<'a> Widget for Crafting<'a> { }) .collect(); ordered_recipes.sort_by_key(|(_, recipe, state)| (*state, recipe.output.0.name())); - match &state.selected_recipe { - None => {}, - Some(recipe) => { - let can_perform = client.available_recipes().contains(recipe.as_str()); - // Ingredients Text - Text::new(&self.localized_strings.get("hud.crafting.ingredients")) - .top_left_with_margins_on(state.ids.align_ing, 10.0, 5.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(18)) - .color(TEXT_COLOR) - .set(state.ids.ingredients_txt, ui); - // Craft button - if Button::image(self.imgs.button) - .w_h(105.0, 25.0) - .hover_image( - can_perform - .then_some(self.imgs.button_hover) - .unwrap_or(self.imgs.button), - ) - .press_image( - can_perform - .then_some(self.imgs.button_press) - .unwrap_or(self.imgs.button), - ) - .label(&self.localized_strings.get("hud.crafting.craft")) - .label_y(conrod_core::position::Relative::Scalar(1.0)) - .label_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR)) - .label_font_size(self.fonts.cyri.scale(12)) - .label_font_id(self.fonts.cyri.conrod_id) - .image_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR)) - .mid_bottom_with_margin_on(state.ids.align_ing, -31.0) - .parent(state.ids.window_frame) - .set(state.ids.btn_craft, ui) - .was_clicked() - { - events.push(Event::CraftRecipe(recipe.clone())); - } - // Result Image BG - let quality_col_img = if let Some(recipe) = state - .selected_recipe - .as_ref() - .and_then(|r| self.client.recipe_book().get(r.as_str())) - { - match recipe.output.0.quality { - Quality::Low => self.imgs.inv_slot_grey, - Quality::Common => self.imgs.inv_slot, - Quality::Moderate => self.imgs.inv_slot_green, - Quality::High => self.imgs.inv_slot_blue, - Quality::Epic => self.imgs.inv_slot_purple, - Quality::Legendary => self.imgs.inv_slot_gold, - Quality::Artifact => self.imgs.inv_slot_orange, - _ => self.imgs.inv_slot_red, - } - } else { - self.imgs.inv_slot - }; - Image::new(quality_col_img) - .w_h(60.0, 60.0) - .top_right_with_margins_on(state.ids.align_ing, 15.0, 10.0) - .parent(state.ids.align_ing) - .set(state.ids.output_img_frame, ui); - - if let Some(recipe) = state - .selected_recipe - .as_ref() - .and_then(|r| self.client.recipe_book().get(r.as_str())) - { - let output_text = format!("x{}", &recipe.output.1.to_string()); - // Output Image - Button::image(animate_by_pulse( - &self - .item_imgs - .img_ids_or_not_found_img((&*recipe.output.0).into()), - self.pulse, - )) - .w_h(55.0, 55.0) - .label(&output_text) - .label_color(TEXT_COLOR) - .label_font_size(self.fonts.cyri.scale(14)) - .label_font_id(self.fonts.cyri.conrod_id) - .label_y(conrod_core::position::Relative::Scalar(-24.0)) - .label_x(conrod_core::position::Relative::Scalar(24.0)) - .middle_of(state.ids.output_img_frame) - .with_item_tooltip( - self.item_tooltip_manager, - &*recipe.output.0, - &None, - &item_tooltip, - ) - .set(state.ids.output_img, ui); - } - }, - } // Recipe list + if state.ids.recipe_names.len() < self.client.recipe_book().iter().len() { + state.update(|state| { + state.ids.recipe_names.resize( + self.client.recipe_book().iter().len(), + &mut ui.widget_id_generator(), + ) + }); + } for (i, (name, recipe, quantity)) in ordered_recipes .into_iter() - .filter(|(_name, recipe, _quantity)| match &self.show.crafting_tab { - SelectedCraftingTab::Food => recipe.output.0.tags().contains(&ItemTag::Food), - SelectedCraftingTab::Armor => match recipe.output.0.kind() { - ItemKind::Armor(_) => !recipe.output.0.tags().contains(&ItemTag::Bag), - _ => false, - }, - SelectedCraftingTab::Glider => { - matches!(recipe.output.0.kind(), ItemKind::Glider(_)) - }, - SelectedCraftingTab::Potion => recipe.output.0.tags().contains(&ItemTag::Potion), - SelectedCraftingTab::Bag => recipe.output.0.tags().contains(&ItemTag::Bag), - SelectedCraftingTab::Tool => { - recipe.output.0.tags().contains(&ItemTag::CraftingTool) - }, - SelectedCraftingTab::Utility => recipe.output.0.tags().contains(&ItemTag::Utility), - SelectedCraftingTab::Weapon => match recipe.output.0.kind() { - ItemKind::Tool(_) => !recipe.output.0.tags().contains(&ItemTag::CraftingTool), - _ => false, - }, - SelectedCraftingTab::Dismantle => match recipe.output.0.kind() { - ItemKind::Ingredient { .. } => { - !recipe.output.0.tags().contains(&ItemTag::CraftingTool) - }, - _ => false, - }, - }) + .filter(|(_, recipe, _)| self.show.crafting_tab.satisfies(recipe.output.0.as_ref())) .enumerate() { let button = Button::image( @@ -562,11 +511,11 @@ impl<'a> Widget for Crafting<'a> { } } - //Ingredients - if let Some(recipe) = state + // Selected Recipe + if let Some((recipe_name, recipe)) = state .selected_recipe .as_ref() - .and_then(|r| self.client.recipe_book().get(r.as_str())) + .and_then(|rn| self.client.recipe_book().get(rn.as_str()).map(|r| (rn, r))) { // Title Text::new(&recipe.output.0.name()) @@ -576,6 +525,132 @@ impl<'a> Widget for Crafting<'a> { .color(TEXT_COLOR) .parent(state.ids.window) .set(state.ids.title_ing, ui); + let can_perform = self + .client + .available_recipes() + .contains(recipe_name.as_str()); + + // Craft button + if Button::image(self.imgs.button) + .w_h(105.0, 25.0) + .hover_image( + can_perform + .then_some(self.imgs.button_hover) + .unwrap_or(self.imgs.button), + ) + .press_image( + can_perform + .then_some(self.imgs.button_press) + .unwrap_or(self.imgs.button), + ) + .label(&self.localized_strings.get("hud.crafting.craft")) + .label_y(conrod_core::position::Relative::Scalar(1.0)) + .label_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR)) + .label_font_size(self.fonts.cyri.scale(12)) + .label_font_id(self.fonts.cyri.conrod_id) + .image_color(can_perform.then_some(TEXT_COLOR).unwrap_or(TEXT_GRAY_COLOR)) + .mid_bottom_with_margin_on(state.ids.align_ing, -31.0) + .parent(state.ids.window_frame) + .set(state.ids.btn_craft, ui) + .was_clicked() + { + events.push(Event::CraftRecipe(recipe_name.clone())); + } + + // Output Image Frame + let quality_col_img = match recipe.output.0.quality { + Quality::Low => self.imgs.inv_slot_grey, + Quality::Common => self.imgs.inv_slot, + Quality::Moderate => self.imgs.inv_slot_green, + Quality::High => self.imgs.inv_slot_blue, + Quality::Epic => self.imgs.inv_slot_purple, + Quality::Legendary => self.imgs.inv_slot_gold, + Quality::Artifact => self.imgs.inv_slot_orange, + _ => self.imgs.inv_slot_red, + }; + + Image::new(quality_col_img) + .w_h(60.0, 60.0) + .top_right_with_margins_on(state.ids.align_ing, 15.0, 10.0) + .parent(state.ids.align_ing) + .set(state.ids.output_img_frame, ui); + + let output_text = format!("x{}", &recipe.output.1.to_string()); + // Output Image + Button::image(animate_by_pulse( + &self + .item_imgs + .img_ids_or_not_found_img((&*recipe.output.0).into()), + self.pulse, + )) + .w_h(55.0, 55.0) + .label(&output_text) + .label_color(TEXT_COLOR) + .label_font_size(self.fonts.cyri.scale(14)) + .label_font_id(self.fonts.cyri.conrod_id) + .label_y(conrod_core::position::Relative::Scalar(-24.0)) + .label_x(conrod_core::position::Relative::Scalar(24.0)) + .middle_of(state.ids.output_img_frame) + .with_item_tooltip( + self.item_tooltip_manager, + &*recipe.output.0, + &None, + &item_tooltip, + ) + .set(state.ids.output_img, ui); + + // Tags + if state.ids.tags_ing.len() < CraftingTab::iter().len() { + state.update(|state| { + state + .ids + .tags_ing + .resize(CraftingTab::iter().len(), &mut ui.widget_id_generator()) + }); + } + for (row, chunk) in CraftingTab::iter() + .filter(|crafting_tab| match crafting_tab { + CraftingTab::All => false, + _ => crafting_tab.satisfies(recipe.output.0.as_ref()), + }) + .filter(|crafting_tab| crafting_tab != &self.show.crafting_tab) + .collect::>() + .chunks(3) + .enumerate() + { + for (col, crafting_tab) in chunk.iter().rev().enumerate() { + let i = 3 * row + col; + let icon = Image::new(crafting_tab.img_id(self.imgs)) + .w_h(20.0, 20.0) + .parent(state.ids.window); + let icon = if col == 0 { + icon.bottom_right_with_margins_on( + state.ids.output_img_frame, + -24.0 - 24.0 * (row as f64), + 4.0, + ) + } else { + icon.left_from(state.ids.tags_ing[i - 1], 4.0) + }; + icon.with_tooltip( + self.tooltip_manager, + &self.localized_strings.get(crafting_tab.name_key()), + "", + &tabs_tooltip, + TEXT_COLOR, + ) + .set(state.ids.tags_ing[i], ui); + } + } + + // Ingredients Text + Text::new(&self.localized_strings.get("hud.crafting.ingredients")) + .top_left_with_margins_on(state.ids.align_ing, 10.0, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(18)) + .color(TEXT_COLOR) + .set(state.ids.ingredients_txt, ui); + // Ingredient images with tooltip if state.ids.ingredient_frame.len() < recipe.inputs().len() { state.update(|state| { @@ -725,6 +800,56 @@ impl<'a> Widget for Crafting<'a> { } } } + + // Search / Title Recipes + if let Some(key) = &self.show.crafting_search_key { + if Button::image(self.imgs.close_btn) + .top_left_with_margins_on(state.ids.align_rec, -20.0, 5.0) + .w_h(14.0, 14.0) + .hover_image(self.imgs.close_btn_hover) + .press_image(self.imgs.close_btn_press) + .parent(state.ids.window) + .set(state.ids.btn_close_search, ui) + .was_clicked() + { + events.push(Event::SearchRecipe(None)); + } + if let Some(string) = TextEdit::new(key.as_str()) + .top_left_with_margins_on(state.ids.btn_close_search, -2.0, 18.0) + .w_h(90.0, 20.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(TEXT_COLOR) + .parent(state.ids.window) + .set(state.ids.input_search, ui) + { + events.push(Event::SearchRecipe(Some(string))); + } + } else { + Text::new(&self.localized_strings.get("hud.crafting.recipes")) + .mid_top_with_margin_on(state.ids.align_rec, -22.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(TEXT_COLOR) + .parent(state.ids.window) + .set(state.ids.title_rec, ui); + Rectangle::fill_with([114.0, 20.0], color::TRANSPARENT) + .top_left_with_margins_on(state.ids.window, 52.0, 26.0) + .graphics_for(state.ids.btn_open_search) + .set(state.ids.input_overlay_search, ui); + if Button::image(self.imgs.search_btn) + .top_left_with_margins_on(state.ids.align_rec, -21.0, 5.0) + .w_h(16.0, 16.0) + .hover_image(self.imgs.search_btn_hover) + .press_image(self.imgs.search_btn_press) + .parent(state.ids.window) + .set(state.ids.btn_open_search, ui) + .was_clicked() + { + events.push(Event::SearchRecipe(Some(String::new()))); + events.push(Event::Focus(state.ids.input_search)); + } + } // Scrollbars Scrollbar::y_axis(state.ids.align_rec) .thickness(5.0) @@ -735,15 +860,6 @@ impl<'a> Widget for Crafting<'a> { .rgba(0.33, 0.33, 0.33, 1.0) .set(state.ids.scrollbar_ing, ui); - // Title Recipes and Ingredients - Text::new(&self.localized_strings.get("hud.crafting.recipes")) - .mid_top_with_margin_on(state.ids.align_rec, -22.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(14)) - .color(TEXT_COLOR) - .parent(state.ids.window) - .set(state.ids.title_rec, ui); - events } } diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 5a746d9c83..634fc9f5f0 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -88,15 +88,6 @@ image_ids! { // Social Window social_frame_on: "voxygen.element.misc_bg.social_frame", social_bg_on: "voxygen.element.misc_bg.social_bg", - social_frame_friends: "voxygen.element.misc_bg.social_frame", - social_bg_friends: "voxygen.element.misc_bg.social_bg", - social_frame_fact: "voxygen.element.misc_bg.social_frame", - social_bg_fact: "voxygen.element.misc_bg.social_bg", - social_tab_act: "voxygen.element.buttons.social_tab_active", - social_tab_online: "voxygen.element.misc_bg.social_tab_online", - social_tab_inact: "voxygen.element.buttons.social_tab_inactive", - social_tab_inact_hover: "voxygen.element.buttons.social_tab_inactive", - social_tab_inact_press: "voxygen.element.buttons.social_tab_inactive", // Crafting Window crafting_window: "voxygen.element.misc_bg.crafting", @@ -110,6 +101,7 @@ image_ids! { icon_dismantle: "voxygen.element.icons.dismantle", icon_food: "voxygen.element.icons.foods", icon_glider: "voxygen.element.icons.gliders", + icon_globe: "voxygen.element.icons.globe", icon_potion: "voxygen.element.icons.potions", icon_utility: "voxygen.element.icons.utilities", icon_weapon: "voxygen.element.icons.weapons", @@ -403,6 +395,10 @@ image_ids! { close_button_hover: "voxygen.element.buttons.close_btn_hover", close_button_press: "voxygen.element.buttons.close_btn_press", + // Search-button + search_btn: "voxygen.element.buttons.search_btn", + search_btn_hover: "voxygen.element.buttons.search_btn_hover", + search_btn_press: "voxygen.element.buttons.search_btn_press", // Inventory collapse_btn: "voxygen.element.buttons.inv_collapse", collapse_btn_hover: "voxygen.element.buttons.inv_collapse_hover", diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 6d4532f566..fdddab5328 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -31,7 +31,7 @@ use buffs::BuffsBar; use buttons::Buttons; use chat::Chat; use chrono::NaiveTime; -use crafting::{Crafting, SelectedCraftingTab}; +use crafting::{Crafting, CraftingTab}; use diary::{Diary, SelectedSkillTree}; use esc_menu::EscMenu; use group::Group; @@ -44,7 +44,7 @@ use prompt_dialog::PromptDialog; use serde::{Deserialize, Serialize}; use settings_window::{SettingsTab, SettingsWindow}; use skillbar::Skillbar; -use social::{Social, SocialTab}; +use social::Social; use trade::Trade; use crate::{ @@ -546,8 +546,9 @@ pub struct Show { ingame: bool, settings_tab: SettingsTab, skilltreetab: SelectedSkillTree, - crafting_tab: SelectedCraftingTab, - social_tab: SocialTab, + crafting_tab: CraftingTab, + crafting_search_key: Option, + social_search_key: Option, want_grab: bool, stats: bool, free_look: bool, @@ -561,6 +562,10 @@ impl Show { self.bag = open; self.map = false; self.want_grab = !open; + + if !open { + self.crafting = false; + } } } @@ -590,6 +595,10 @@ impl Show { fn social(&mut self, open: bool) { if !self.esc_menu { + if !self.social && open { + // rising edge detector + self.search_social_players(None); + } self.social = open; self.diary = false; self.want_grab = !open; @@ -598,6 +607,10 @@ impl Show { fn crafting(&mut self, open: bool) { if !self.esc_menu { + if !self.crafting && open { + // rising edge detector + self.search_crafting_recipe(None); + } self.crafting = open; self.bag = open; self.map = false; @@ -711,11 +724,6 @@ impl Show { fn toggle_crafting(&mut self) { self.crafting(!self.crafting) } - fn open_social_tab(&mut self, social_tab: SocialTab) { - self.social_tab = social_tab; - self.diary = false; - } - fn toggle_spell(&mut self) { self.diary = !self.diary; self.bag = false; @@ -730,8 +738,14 @@ impl Show { self.social = false; } - fn selected_crafting_tab(&mut self, sel_cat: SelectedCraftingTab) { - self.crafting_tab = sel_cat; + fn selected_crafting_tab(&mut self, sel_cat: CraftingTab) { self.crafting_tab = sel_cat; } + + fn search_crafting_recipe(&mut self, search_key: Option) { + self.crafting_search_key = search_key; + } + + fn search_social_players(&mut self, search_key: Option) { + self.social_search_key = search_key; } /// If all of the menus are closed, adjusts coordinates of cursor to center @@ -890,8 +904,9 @@ impl Hud { group_menu: false, settings_tab: SettingsTab::Interface, skilltreetab: SelectedSkillTree::General, - crafting_tab: SelectedCraftingTab::Armor, - social_tab: SocialTab::Online, + crafting_tab: CraftingTab::All, + crafting_search_key: None, + social_search_key: None, want_grab: true, ingame: true, stats: false, @@ -2368,7 +2383,6 @@ impl Hud { Some(bag::Event::Close) => { self.show.stats = false; self.show.bag(false); - self.show.crafting(false); if !self.show.social { self.show.want_grab = true; self.force_ungrab = false; @@ -2469,7 +2483,6 @@ impl Hud { crafting::Event::Close => { self.show.stats = false; self.show.crafting(false); - self.show.bag(false); if !self.show.social { self.show.want_grab = true; self.force_ungrab = false; @@ -2480,6 +2493,12 @@ impl Hud { crafting::Event::ChangeCraftingTab(sel_cat) => { self.show.selected_crafting_tab(sel_cat); }, + crafting::Event::Focus(widget_id) => { + self.to_focus = Some(Some(widget_id)); + }, + crafting::Event::SearchRecipe(search_key) => { + self.show.search_crafting_recipe(search_key); + }, } } } @@ -2735,10 +2754,13 @@ impl Hud { self.force_ungrab = true }; }, - social::Event::ChangeSocialTab(social_tab) => { - self.show.open_social_tab(social_tab) + social::Event::Focus(widget_id) => { + self.to_focus = Some(Some(widget_id)); }, social::Event::Invite(uid) => events.push(Event::InviteMember(uid)), + social::Event::SearchPlayers(search_key) => { + self.show.search_social_players(search_key) + }, } } } diff --git a/voxygen/src/hud/social.rs b/voxygen/src/hud/social.rs index ee7e6b7a39..2fa67f27b5 100644 --- a/voxygen/src/hud/social.rs +++ b/voxygen/src/hud/social.rs @@ -11,7 +11,7 @@ use client::{self, Client}; use common::{comp::group, uid::Uid}; use conrod_core::{ color, - widget::{self, Button, Image, Rectangle, Scrollbar, Text}, + widget::{self, Button, Image, Rectangle, Scrollbar, Text, TextEdit}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; use itertools::Itertools; @@ -27,25 +27,14 @@ widget_ids! { icon, scrollbar, online_align, - online_tab, - names_align, - name_txt, - player_levels[], player_names[], - player_zones[], online_txt, online_no, - levels_align, - level_txt, - zones_align, - zone_txt, - friends_tab, - //friends_tab_icon, - faction_tab, - //faction_tab_icon, - friends_test, - faction_test, invite_button, + player_search_icon, + player_search_input, + player_search_input_bg, + player_search_input_overlay, } } @@ -56,12 +45,6 @@ pub struct State { selected_uid: Option<(Uid, Instant)>, } -pub enum SocialTab { - Online, - Friends, - Faction, -} - #[derive(WidgetCommon)] pub struct Social<'a> { show: &'a Show, @@ -106,7 +89,8 @@ impl<'a> Social<'a> { pub enum Event { Close, Invite(Uid), - ChangeSocialTab(SocialTab), + Focus(widget::Id), + SearchPlayers(Option), } impl<'a> Widget for Social<'a> { @@ -145,33 +129,27 @@ impl<'a> Widget for Social<'a> { .font_id(self.fonts.cyri.conrod_id) .desc_text_color(TEXT_COLOR); - // Window frame and BG - let pos = if self.show.group || self.show.group_menu { - 200.0 - } else { - 25.0 - }; - // TODO: Different window visuals depending on the selected tab - let window_bg = match &self.show.social_tab { - SocialTab::Online => self.imgs.social_bg_on, - SocialTab::Friends => self.imgs.social_bg_friends, - SocialTab::Faction => self.imgs.social_bg_fact, - }; - let window_frame = match &self.show.social_tab { - SocialTab::Online => self.imgs.social_frame_on, - SocialTab::Friends => self.imgs.social_frame_friends, - SocialTab::Faction => self.imgs.social_frame_fact, - }; - Image::new(window_bg) - .bottom_left_with_margins_on(ui.window, 308.0, pos) + // Window BG + Image::new(self.imgs.social_bg_on) + .bottom_left_with_margins_on(ui.window, 308.0, 25.0) .color(Some(UI_MAIN)) .w_h(280.0, 460.0) .set(state.ids.bg, ui); - Image::new(window_frame) + // Search Background + // I couldn't find a way to manually set they layer of a widget + // If it is possible, please move this code (for rectangle) down to the code for + // search input + Rectangle::fill([248.0, 20.0]) + .top_left_with_margins_on(state.ids.bg, 52.0, 27.0) + .hsla(0.0, 0.0, 0.0, 0.7) + .set(state.ids.player_search_input_bg, ui); + // Window frame + Image::new(self.imgs.social_frame_on) .middle_of(state.ids.bg) .color(Some(UI_HIGHLIGHT_0)) .w_h(280.0, 460.0) .set(state.ids.frame, ui); + // Icon Image::new(self.imgs.social) .w_h(30.0, 30.0) @@ -200,381 +178,269 @@ impl<'a> Widget for Social<'a> { .color(TEXT_COLOR) .set(state.ids.title, ui); - // Tabs Buttons - // Online Tab Button - if Button::image(match &self.show.social_tab { - SocialTab::Online => self.imgs.social_tab_online, - _ => self.imgs.social_tab_inact, - }) - .w_h(30.0, 44.0) - .image_color(match &self.show.social_tab { - SocialTab::Online => UI_MAIN, - _ => Color::Rgba(1.0, 1.0, 1.0, 0.6), - }) - .top_right_with_margins_on(state.ids.frame, 50.0, -27.0) - .set(state.ids.online_tab, ui) - .was_clicked() - { - events.push(Event::ChangeSocialTab(SocialTab::Online)); - } - // Friends Tab Button - if Button::image(match &self.show.social_tab { - SocialTab::Friends => self.imgs.social_tab_act, - _ => self.imgs.social_tab_inact, - }) - .w_h(30.0, 44.0) - .hover_image(match &self.show.social_tab { - SocialTab::Friends => self.imgs.social_tab_act, - _ => self.imgs.social_tab_inact_hover, - }) - .press_image(match &self.show.social_tab { - SocialTab::Friends => self.imgs.social_tab_act, - _ => self.imgs.social_tab_inact_press, - }) - .down_from(state.ids.online_tab, 0.0) - .image_color(match &self.show.social_tab { - SocialTab::Friends => UI_MAIN, - _ => Color::Rgba(1.0, 1.0, 1.0, 0.6), - }) - .set(state.ids.friends_tab, ui) - .was_clicked() - { - events.push(Event::ChangeSocialTab(SocialTab::Friends)); - } - // Faction Tab Button - if Button::image(match &self.show.social_tab { - SocialTab::Friends => self.imgs.social_tab_act, - _ => self.imgs.social_tab_inact, - }) - .w_h(30.0, 44.0) - .hover_image(match &self.show.social_tab { - SocialTab::Faction => self.imgs.social_tab_act, - _ => self.imgs.social_tab_inact_hover, - }) - .press_image(match &self.show.social_tab { - SocialTab::Faction => self.imgs.social_tab_act, - _ => self.imgs.social_tab_inact_press, - }) - .down_from(state.ids.friends_tab, 0.0) - .image_color(match &self.show.social_tab { - SocialTab::Faction => UI_MAIN, - _ => Color::Rgba(1.0, 1.0, 1.0, 0.6), - }) - .set(state.ids.faction_tab, ui) - .was_clicked() - { - events.push(Event::ChangeSocialTab(SocialTab::Faction)); - } - // Online Tab - if let SocialTab::Online = self.show.social_tab { - let players = self - .client - .player_list() - .iter() - .filter(|(_, p)| p.is_online); - let count = players.clone().count(); - let height = if count > 1 { - count as f64 - 1.0 + 20.0 * count as f64 - 1.0 - } else { - 1.0 - }; - // Content Alignments - Rectangle::fill_with([270.0, 346.0], color::TRANSPARENT) - .mid_top_with_margin_on(state.ids.frame, 74.0) - .scroll_kids_vertically() - .set(state.ids.online_align, ui); - Rectangle::fill_with([133.0, height], color::TRANSPARENT) - .top_left_with_margins_on(state.ids.online_align, 0.0, 0.0) - .crop_kids() - .set(state.ids.names_align, ui); - Rectangle::fill_with([39.0, height], color::TRANSPARENT) - .right_from(state.ids.names_align, 2.0) - .crop_kids() - .set(state.ids.levels_align, ui); - Rectangle::fill_with([94.0, height], color::TRANSPARENT) - .right_from(state.ids.levels_align, 2.0) - .crop_kids() - .set(state.ids.zones_align, ui); - Scrollbar::y_axis(state.ids.online_align) - .thickness(4.0) - .color(Color::Rgba(0.79, 1.09, 1.09, 0.0)) - .set(state.ids.scrollbar, ui); - // - // Headlines - // - if Button::image(self.imgs.nothing) - .w_h(133.0, 18.0) - .mid_top_with_margin_on(state.ids.frame, 52.0) - .label(&self.localized_strings.get("")) - .label_font_size(self.fonts.cyri.scale(14)) - .label_y(conrod_core::position::Relative::Scalar(0.0)) - .label_font_id(self.fonts.cyri.conrod_id) - .label_color(TEXT_COLOR) - .set(state.ids.name_txt, ui) - .was_clicked() - { - // Sort widgets by name alphabetically - } - if Button::image(self.imgs.nothing) - .w_h(39.0, 18.0) - .right_from(state.ids.name_txt, 2.0) - .label("") - .label_font_size(self.fonts.cyri.scale(14)) - .label_y(conrod_core::position::Relative::Scalar(0.0)) - .label_font_id(self.fonts.cyri.conrod_id) - .label_color(TEXT_COLOR) - .set(state.ids.level_txt, ui) - .was_clicked() - { - // Sort widgets by level (increasing) - } - if Button::image(self.imgs.nothing) - .w_h(93.0, 18.0) - .right_from(state.ids.level_txt, 2.0) - .label("") // TODO: Enable zone here later - .label_font_size(self.fonts.cyri.scale(14)) - .label_y(conrod_core::position::Relative::Scalar(0.0)) - .label_font_id(self.fonts.cyri.conrod_id) - .label_color(TEXT_COLOR) - .set(state.ids.zone_txt, ui) - .was_clicked() - { - // Sort widgets by zone alphabetically - } - // Online Text - Text::new(&self.localized_strings.get("hud.social.online")) - .bottom_left_with_margins_on(state.ids.frame, 18.0, 10.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(14)) - .color(TEXT_COLOR) - .set(state.ids.online_txt, ui); - Text::new(&count.to_string()) - .right_from(state.ids.online_txt, 5.0) - .font_id(self.fonts.cyri.conrod_id) - .font_size(self.fonts.cyri.scale(14)) - .color(TEXT_COLOR) - .set(state.ids.online_no, ui); - // Adjust widget_id struct vec length to player count - if state.ids.player_levels.len() < count { - state.update(|s| { - s.ids - .player_levels - .resize(count, &mut ui.widget_id_generator()) - }) - }; - if state.ids.player_names.len() < count { - state.update(|s| { - s.ids - .player_names - .resize(count, &mut ui.widget_id_generator()) - }) - }; - if state.ids.player_zones.len() < count { - state.update(|s| { - s.ids - .player_zones - .resize(count, &mut ui.widget_id_generator()) - }) - }; - // Create a name, level and zone row for every player in the list - // Filter out yourself from the online list - let my_uid = self.client.uid(); - let mut player_list = players - .filter(|(uid, _)| Some(**uid) != my_uid) - .collect_vec(); - player_list.sort_by_key(|(_, player)| { - player - .character + let players = self + .client + .player_list() + .iter() + .filter(|(_, p)| p.is_online); + let player_count = players.clone().count(); + + // Content Alignment + Rectangle::fill_with([270.0, 346.0], color::TRANSPARENT) + .mid_top_with_margin_on(state.ids.frame, 74.0) + .scroll_kids_vertically() + .set(state.ids.online_align, ui); + Scrollbar::y_axis(state.ids.online_align) + .thickness(4.0) + .color(Color::Rgba(0.79, 1.09, 1.09, 0.0)) + .set(state.ids.scrollbar, ui); + + // Online Text + Text::new(&self.localized_strings.get("hud.social.online")) + .bottom_left_with_margins_on(state.ids.frame, 18.0, 10.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(TEXT_COLOR) + .set(state.ids.online_txt, ui); + Text::new(&player_count.to_string()) + .right_from(state.ids.online_txt, 5.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(TEXT_COLOR) + .set(state.ids.online_no, ui); + // Adjust widget_id struct vec length to player count + if state.ids.player_names.len() < player_count { + state.update(|s| { + s.ids + .player_names + .resize(player_count, &mut ui.widget_id_generator()) + }) + }; + + // Filter out yourself from the online list and perform search + let my_uid = self.client.uid(); + let mut player_list = players + .filter(|(uid, _)| Some(**uid) != my_uid) + .filter(|(_, player)| { + self.show + .social_search_key .as_ref() - .map(|character| character.name.to_lowercase()) - .unwrap_or_else(|| player.player_alias.to_string()) - }); - for (i, (&uid, player_info)) in player_list.into_iter().enumerate() { - let hide_username = true; - let zone = ""; // TODO Add real zone - let selected = state.selected_uid.map_or(false, |u| u.0 == uid); - let alias = &player_info.player_alias; - let zone_name = match &player_info.character { - None => self.localized_strings.get("hud.group.in_menu").to_string(), /* character select or spectating */ - _ => format!("{} ", &zone), - }; - let name_text = match &player_info.character { - Some(character) => { - if hide_username { - character.name.to_string() - } else { - format!("[{}] {}", alias, &character.name) - } - }, - None => format!("{} [{}]", alias.clone(), zone_name), /* character select or - * spectating */ - }; - // Player name widgets - let button = Button::image(if !selected { - self.imgs.nothing - } else { - self.imgs.selection - }); - let button = if i == 0 { - button.mid_top_with_margin_on(state.ids.online_align, 1.0) - } else { - button.down_from(state.ids.player_names[i - 1], 1.0) - }; - let acc_name_txt = format!( - "{}: {}", - &self.localized_strings.get("hud.social.account"), - alias - ); - button - .w_h(260.0, 20.0) - .hover_image(if selected { - self.imgs.selection - } else { - self.imgs.selection_hover + .map(|search_key| { + search_key + .to_lowercase() + .split_whitespace() + .all(|substring| { + let player_alias = &player.player_alias.to_lowercase(); + let character_name = player + .character + .as_ref() + .map(|character| character.name.to_lowercase()); + player_alias.contains(substring) + || character_name + .map(|cn| cn.contains(substring)) + .unwrap_or(false) + }) }) - .press_image(if selected { - self.imgs.selection + .unwrap_or(true) + }) + .collect_vec(); + player_list.sort_by_key(|(_, player)| { + player + .character + .as_ref() + .map(|character| &character.name) + .unwrap_or(&player.player_alias) + .to_lowercase() + }); + for (i, (&uid, player_info)) in player_list.into_iter().enumerate() { + let hide_username = true; + let selected = state.selected_uid.map_or(false, |u| u.0 == uid); + let alias = &player_info.player_alias; + let name_text = match &player_info.character { + Some(character) => { + if hide_username { + character.name.to_string() } else { - self.imgs.selection_press - }) - .label(&name_text) - .label_font_size(self.fonts.cyri.scale(14)) - .label_y(conrod_core::position::Relative::Scalar(1.0)) - .label_font_id(self.fonts.cyri.conrod_id) - .label_color(TEXT_COLOR) - .with_tooltip( - self.tooltip_manager, - &acc_name_txt, - "", - &button_tooltip, - TEXT_COLOR, - ) - .set(state.ids.player_names[i], ui); - // Player Zones - Button::image(if !selected { - self.imgs.nothing - } else { - self.imgs.selection - }) - .w_h(94.0, 20.0) - .right_from(state.ids.player_levels[i], 2.0) - .label(&zone_name) + format!("[{}] {}", alias, &character.name) + } + }, + None => format!( + "{} [{}]", + alias.clone(), + self.localized_strings.get("hud.group.in_menu").to_string() + ), // character select or spectating + }; + let acc_name_txt = format!( + "{}: {}", + &self.localized_strings.get("hud.social.account"), + alias + ); + // Player name widget + let button = Button::image(if !selected { + self.imgs.nothing + } else { + self.imgs.selection + }) + .hover_image(if selected { + self.imgs.selection + } else { + self.imgs.selection_hover + }) + .press_image(if selected { + self.imgs.selection + } else { + self.imgs.selection_press + }) + .w_h(260.0, 20.0); + let button = if i == 0 { + button.mid_top_with_margin_on(state.ids.online_align, 1.0) + } else { + button.down_from(state.ids.player_names[i - 1], 1.0) + }; + if button + .label(&name_text) .label_font_size(self.fonts.cyri.scale(14)) + .label_y(conrod_core::position::Relative::Scalar(1.0)) .label_font_id(self.fonts.cyri.conrod_id) .label_color(TEXT_COLOR) - .label_y(conrod_core::position::Relative::Scalar(1.0)) - .parent(state.ids.zones_align) - .set(state.ids.player_zones[i], ui); - // Check for click - if ui - .widget_input(state.ids.player_names[i]) - .clicks() - .left() - .next() - .is_some() - { - state.update(|s| s.selected_uid = Some((uid, Instant::now()))); - } - } - - // Invite Button - let is_leader_or_not_in_group = self - .client - .group_info() - .map_or(true, |(_, l_uid)| self.client.uid() == Some(l_uid)); - - let current_members = self - .client - .group_members() - .iter() - .filter(|(_, role)| matches!(role, group::Role::Member)) - .count() - + 1; - let current_invites = self.client.pending_invites().len(); - let max_members = self.client.max_group_size() as usize; - let group_not_full = current_members + current_invites < max_members; - let selected_to_invite = (is_leader_or_not_in_group && group_not_full) - .then(|| { - state - .selected_uid - .as_ref() - .map(|(s, _)| *s) - .filter(|selected| { - self.client.player_list().get(selected).map_or( - false, - |selected_player| { - selected_player.is_online && selected_player.character.is_some() - }, - ) - }) - .or_else(|| { - self.selected_entity - .and_then(|s| self.client.state().read_component_copied(s.0)) - }) - .filter(|selected| { - // Prevent inviting entities already in the same group - !self.client.group_members().contains_key(selected) - }) - }) - .flatten(); - - let invite_button = Button::image(self.imgs.button) - .w_h(106.0, 26.0) - .bottom_right_with_margins_on(state.ids.frame, 9.0, 7.0) - .hover_image(if selected_to_invite.is_some() { - self.imgs.button_hover - } else { - self.imgs.button - }) - .press_image(if selected_to_invite.is_some() { - self.imgs.button_press - } else { - self.imgs.button - }) - .label(self.localized_strings.get("hud.group.invite")) - .label_y(conrod_core::position::Relative::Scalar(3.0)) - .label_color(if selected_to_invite.is_some() { - TEXT_COLOR - } else { - TEXT_COLOR_3 - }) - .image_color(if selected_to_invite.is_some() { - TEXT_COLOR - } else { - TEXT_COLOR_3 - }) - .label_font_size(self.fonts.cyri.scale(15)) - .label_font_id(self.fonts.cyri.conrod_id); - - if if self.client.group_info().is_some() { - let tooltip_txt = format!( - "{}/{} {}", - current_members + current_invites, - max_members, - &self.localized_strings.get("hud.group.members") - ); - invite_button - .with_tooltip( - self.tooltip_manager, - &tooltip_txt, - "", - &button_tooltip, - TEXT_COLOR, - ) - .set(state.ids.invite_button, ui) - } else { - invite_button.set(state.ids.invite_button, ui) - } - .was_clicked() + .with_tooltip( + self.tooltip_manager, + &acc_name_txt, + "", + &button_tooltip, + TEXT_COLOR, + ) + .set(state.ids.player_names[i], ui) + .was_clicked() { - if let Some(uid) = selected_to_invite { - events.push(Event::Invite(uid)); - state.update(|s| { - s.selected_uid = None; - }); - } + state.update(|s| s.selected_uid = Some((uid, Instant::now()))); } - } // End of Online Tab + } + + // Invite Button + let is_leader_or_not_in_group = self + .client + .group_info() + .map_or(true, |(_, l_uid)| self.client.uid() == Some(l_uid)); + + let current_members = self + .client + .group_members() + .iter() + .filter(|(_, role)| matches!(role, group::Role::Member)) + .count() + + 1; + let current_invites = self.client.pending_invites().len(); + let max_members = self.client.max_group_size() as usize; + let group_not_full = current_members + current_invites < max_members; + let selected_to_invite = (is_leader_or_not_in_group && group_not_full) + .then(|| { + state + .selected_uid + .as_ref() + .map(|(s, _)| *s) + .filter(|selected| { + self.client + .player_list() + .get(selected) + .map_or(false, |selected_player| { + selected_player.is_online && selected_player.character.is_some() + }) + }) + .or_else(|| { + self.selected_entity + .and_then(|s| self.client.state().read_component_copied(s.0)) + }) + .filter(|selected| { + // Prevent inviting entities already in the same group + !self.client.group_members().contains_key(selected) + }) + }) + .flatten(); + + let invite_button = Button::image(self.imgs.button) + .w_h(106.0, 26.0) + .bottom_right_with_margins_on(state.ids.frame, 9.0, 7.0) + .hover_image(if selected_to_invite.is_some() { + self.imgs.button_hover + } else { + self.imgs.button + }) + .press_image(if selected_to_invite.is_some() { + self.imgs.button_press + } else { + self.imgs.button + }) + .label(self.localized_strings.get("hud.group.invite")) + .label_y(conrod_core::position::Relative::Scalar(3.0)) + .label_color(if selected_to_invite.is_some() { + TEXT_COLOR + } else { + TEXT_COLOR_3 + }) + .image_color(if selected_to_invite.is_some() { + TEXT_COLOR + } else { + TEXT_COLOR_3 + }) + .label_font_size(self.fonts.cyri.scale(15)) + .label_font_id(self.fonts.cyri.conrod_id); + + if if self.client.group_info().is_some() { + let tooltip_txt = format!( + "{}/{} {}", + current_members + current_invites, + max_members, + &self.localized_strings.get("hud.group.members") + ); + invite_button + .with_tooltip( + self.tooltip_manager, + &tooltip_txt, + "", + &button_tooltip, + TEXT_COLOR, + ) + .set(state.ids.invite_button, ui) + } else { + invite_button.set(state.ids.invite_button, ui) + } + .was_clicked() + { + if let Some(uid) = selected_to_invite { + events.push(Event::Invite(uid)); + state.update(|s| { + s.selected_uid = None; + }); + } + } + + // Player Search + if Button::image(self.imgs.search_btn) + .top_left_with_margins_on(state.ids.frame, 54.0, 9.0) + .hover_image(self.imgs.search_btn_hover) + .press_image(self.imgs.search_btn_press) + .w_h(16.0, 16.0) + .set(state.ids.player_search_icon, ui) + .was_clicked() + { + events.push(Event::Focus(state.ids.player_search_input)); + } + if let Some(string) = + TextEdit::new(self.show.social_search_key.as_deref().unwrap_or_default()) + .top_left_with_margins_on(state.ids.player_search_icon, -1.0, 22.0) + .w_h(215.0, 20.0) + .font_id(self.fonts.cyri.conrod_id) + .font_size(self.fonts.cyri.scale(14)) + .color(TEXT_COLOR) + .set(state.ids.player_search_input, ui) + { + events.push(Event::SearchPlayers(Some(string))); + } + Rectangle::fill_with([266.0, 20.0], color::TRANSPARENT) + .top_left_with_margins_on(state.ids.player_search_icon, -1.0, 0.0) + .graphics_for(state.ids.player_search_icon) + .set(state.ids.player_search_input_overlay, ui); events }