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 0000000000..a218937d12 --- /dev/null +++ b/assets/voxygen/element/buttons/search_btn.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3825377a97cd50dfa7766c09926b2ca00384d58f76d081ebeb7bbdf50e12f9c9 +size 151 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 0000000000..fa0c8755df --- /dev/null +++ b/assets/voxygen/element/buttons/search_btn_hover.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a479ba694bdaa67c952118b128734b6c40a8286327e9438cc791b6403dfbef87 +size 1725 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 0000000000..9d42e6bdb8 --- /dev/null +++ b/assets/voxygen/element/buttons/search_btn_press.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:62efc59177cca194345fa13aa42884db905313c3acd228c3ff7b0baddee06607 +size 1705 diff --git a/assets/voxygen/element/buttons/social_tab.png b/assets/voxygen/element/buttons/social_tab.png deleted file mode 100644 index bd778be483..0000000000 --- a/assets/voxygen/element/buttons/social_tab.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5e30786d526a5d51b5b8b7803ea06f4f8ab6cdfd9abae9d73dc6d03cc0ded279 -size 146 diff --git a/assets/voxygen/element/buttons/social_tab_active.png b/assets/voxygen/element/buttons/social_tab_active.png deleted file mode 100644 index 854d4e224a..0000000000 --- a/assets/voxygen/element/buttons/social_tab_active.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5a641284ee155e1c9e80fc05d214303195de4469b1ad372127db89244aa2e82b -size 470 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 9bc655c891..0000000000 --- a/assets/voxygen/element/buttons/social_tab_hover.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:259504971cb3556ac8ff9334948f86aadd2802ccd11cd0312111311f91594889 -size 190 diff --git a/assets/voxygen/element/buttons/social_tab_inactive.png b/assets/voxygen/element/buttons/social_tab_inactive.png deleted file mode 100644 index c974c14514..0000000000 --- a/assets/voxygen/element/buttons/social_tab_inactive.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:46fbea0721ccf1fea5963c2745b049fe441c1f25049af2882f10bea881fa67f1 -size 477 diff --git a/assets/voxygen/element/buttons/social_tab_press.png b/assets/voxygen/element/buttons/social_tab_press.png deleted file mode 100644 index 55a860b6ea..0000000000 --- a/assets/voxygen/element/buttons/social_tab_press.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c4cf97702ed6bda6816e3db9c73e4d79edb80ff3b36f46a6655aa4ec2b8f799a -size 161 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 f08a6d844c..0000000000 --- a/assets/voxygen/element/buttons/social_tab_pressed.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:39f0f6f47134cebd56389348843bd632dd33c2955d03d4ec36a5afe4bbfed5c3 -size 128 diff --git a/assets/voxygen/element/icons/globe.png b/assets/voxygen/element/icons/globe.png new file mode 100644 index 0000000000..1e59b07417 --- /dev/null +++ b/assets/voxygen/element/icons/globe.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5289a6bc58e757f3b4cbf11a5199acb29ad23ca63287db63e42ebe83fdc4545 +size 278 diff --git a/assets/voxygen/element/icons/globe_2.png b/assets/voxygen/element/icons/globe_2.png new file mode 100644 index 0000000000..84ce016de0 --- /dev/null +++ b/assets/voxygen/element/icons/globe_2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abe3450c47e10824534a93b08564e89e0c3997a2c729c5c4fab69609dd087d1f +size 273 diff --git a/assets/voxygen/element/misc_bg/crafting.png b/assets/voxygen/element/misc_bg/crafting.png index bd6cc4ac0d..2c190b973b 100644 --- a/assets/voxygen/element/misc_bg/crafting.png +++ b/assets/voxygen/element/misc_bg/crafting.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1800710a64a8bb5be612f4cc6c88acd2c560d9125cf69899b5f35bf9e137aa42 -size 7737 +oid sha256:0a179dad099f00995c36664f9a83144010994513558d4d1e2c22d4e0507f9e58 +size 7805 diff --git a/assets/voxygen/element/misc_bg/crafting_frame.png b/assets/voxygen/element/misc_bg/crafting_frame.png index 602b96bc20..8c0fe77191 100644 --- a/assets/voxygen/element/misc_bg/crafting_frame.png +++ b/assets/voxygen/element/misc_bg/crafting_frame.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfa17b2872fc21a8974e3955c14efda8017e87a8de9f72f42b9d5207eeaa1053 -size 15174 +oid sha256:5e20ab30a61728e1dd77573880596504c1e5ea9dbea9e1f1f2fa5bd05f67bd77 +size 8189 diff --git a/assets/voxygen/element/misc_bg/social_bg.png b/assets/voxygen/element/misc_bg/social_bg.png index 47e0a7f419..ea3c79eb1c 100644 --- a/assets/voxygen/element/misc_bg/social_bg.png +++ b/assets/voxygen/element/misc_bg/social_bg.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16787447875fa7af882cfdd6c049e96050cacd4fc84cdbff7ab1d9a516149787 -size 6109 +oid sha256:67e0e536a9de0fd6a6485a6d5e3826a99d1c369cd0c378d67e567e4fb64e295c +size 5842 diff --git a/assets/voxygen/element/misc_bg/social_frame.png b/assets/voxygen/element/misc_bg/social_frame.png index ca73967d15..543f868d60 100644 --- a/assets/voxygen/element/misc_bg/social_frame.png +++ b/assets/voxygen/element/misc_bg/social_frame.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da031950b47f5536cd461aa357a5a8c5ec197c79f89e21ff89209a91f391441a -size 6579 +oid sha256:4b471a7e4fc2e760cffd45a9463d7b390480766159095c4172c5dd3305c0e17f +size 4768 diff --git a/assets/voxygen/i18n/en/hud/crafting.ron b/assets/voxygen/i18n/en/hud/crafting.ron index 5dd30c3fd3..41d5cf4a49 100644 --- a/assets/voxygen/i18n/en/hud/crafting.ron +++ b/assets/voxygen/i18n/en/hud/crafting.ron @@ -8,6 +8,18 @@ "hud.crafting.ingredients": "Ingredients:", "hud.crafting.craft": "Craft", "hud.crafting.tool_cata": "Requires:", + + // Tabs + "hud.crafting.tabs.all": "All", + "hud.crafting.tabs.armor": "Armor", + "hud.crafting.tabs.dismantle": "Dismantle", + "hud.crafting.tabs.food": "Food", + "hud.crafting.tabs.glider": "Gliders", + "hud.crafting.tabs.potion": "Potions", + "hud.crafting.tabs.tool": "Tools", + "hud.crafting.tabs.utility": "Utility", + "hud.crafting.tabs.weapon": "Weapons", + "hud.crafting.tabs.bag": "Bags", }, diff --git a/voxygen/src/hud/crafting.rs b/voxygen/src/hud/crafting.rs index 9219b70714..2867bb36dd 100644 --- a/voxygen/src/hud/crafting.rs +++ b/voxygen/src/hud/crafting.rs @@ -22,8 +22,8 @@ use common::{ recipe::RecipeInput, }; use conrod_core::{ - color, - widget::{self, Button, Image, Rectangle, Scrollbar, Text}, + color, image, + widget::{self, Button, Image, Rectangle, Scrollbar, Text, TextEdit}, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, }; use std::sync::Arc; @@ -41,7 +41,13 @@ widget_ids! { title_rec, align_rec, scrollbar_rec, + btn_open_search, + btn_close_search, + input_search, + input_bg_search, + input_overlay_search, title_ing, + tags_ing[], align_ing, scrollbar_ing, btn_craft, @@ -64,8 +70,10 @@ widget_ids! { pub enum Event { CraftRecipe(String), - ChangeCraftingTab(SelectedCraftingTab), + ChangeCraftingTab(CraftingTab), Close, + Focus(widget::Id), + SearchRecipe(Option), } #[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 }