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

This commit is contained in:
hqurve 2021-04-07 20:24:22 +00:00 committed by Ben Wallis
parent 60efd682e2
commit be42cc60c4
21 changed files with 664 additions and 653 deletions

View File

@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Crafting menu tabs - Crafting menu tabs
- Auto camera setting, making the game easier to play with one hand - Auto camera setting, making the game easier to play with one hand
- Topographic map option - Topographic map option
- Search bars for social and crafting window
### Changed ### 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 - Added a raycast check to beams to prevent their effect applying through walls
- Flying agents raycast more angles to check for obstacles. - Flying agents raycast more angles to check for obstacles.
- Mouse Cursor now locks to the center of the screen when menu is not open - 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 ## [0.9.0] - 2021-03-20

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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/voxygen/element/icons/globe.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
assets/voxygen/element/icons/globe_2.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -8,6 +8,18 @@
"hud.crafting.ingredients": "Ingredients:", "hud.crafting.ingredients": "Ingredients:",
"hud.crafting.craft": "Craft", "hud.crafting.craft": "Craft",
"hud.crafting.tool_cata": "Requires:", "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",
}, },

View File

@ -22,8 +22,8 @@ use common::{
recipe::RecipeInput, recipe::RecipeInput,
}; };
use conrod_core::{ use conrod_core::{
color, color, image,
widget::{self, Button, Image, Rectangle, Scrollbar, Text}, widget::{self, Button, Image, Rectangle, Scrollbar, Text, TextEdit},
widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
}; };
use std::sync::Arc; use std::sync::Arc;
@ -41,7 +41,13 @@ widget_ids! {
title_rec, title_rec,
align_rec, align_rec,
scrollbar_rec, scrollbar_rec,
btn_open_search,
btn_close_search,
input_search,
input_bg_search,
input_overlay_search,
title_ing, title_ing,
tags_ing[],
align_ing, align_ing,
scrollbar_ing, scrollbar_ing,
btn_craft, btn_craft,
@ -64,8 +70,10 @@ widget_ids! {
pub enum Event { pub enum Event {
CraftRecipe(String), CraftRecipe(String),
ChangeCraftingTab(SelectedCraftingTab), ChangeCraftingTab(CraftingTab),
Close, Close,
Focus(widget::Id),
SearchRecipe(Option<String>),
} }
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
@ -120,7 +128,8 @@ impl<'a> Crafting<'a> {
} }
#[derive(Debug, EnumIter, PartialEq)] #[derive(Debug, EnumIter, PartialEq)]
pub enum SelectedCraftingTab { pub enum CraftingTab {
All,
Armor, Armor,
Weapon, Weapon,
Food, Food,
@ -131,6 +140,61 @@ pub enum SelectedCraftingTab {
Utility, Utility,
Glider, 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 { pub struct State {
ids: Ids, ids: Ids,
@ -155,15 +219,6 @@ impl<'a> Widget for Crafting<'a> {
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event { fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { state, ui, .. } = args; 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(); let mut events = Vec::new();
// Tooltips // Tooltips
@ -211,21 +266,36 @@ impl<'a> Widget for Crafting<'a> {
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.desc_text_color(TEXT_COLOR); .desc_text_color(TEXT_COLOR);
// Frame and window
Image::new(self.imgs.crafting_window) Image::new(self.imgs.crafting_window)
.bottom_right_with_margins_on(ui.window, 308.0, 450.0) .bottom_right_with_margins_on(ui.window, 308.0, 450.0)
.color(Some(UI_MAIN)) .color(Some(UI_MAIN))
.w_h(422.0, 460.0) .w_h(422.0, 460.0)
.set(state.ids.window, ui); .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) Image::new(self.imgs.crafting_frame)
.middle_of(state.ids.window) .middle_of(state.ids.window)
.color(Some(UI_HIGHLIGHT_0)) .color(Some(UI_HIGHLIGHT_0))
.w_h(422.0, 460.0) .w_h(422.0, 460.0)
.set(state.ids.window_frame, ui); .set(state.ids.window_frame, ui);
// Crafting Icon
Image::new(self.imgs.crafting_icon_bordered) Image::new(self.imgs.crafting_icon_bordered)
.w_h(38.0, 38.0) .w_h(38.0, 38.0)
.top_left_with_margins_on(state.ids.window_frame, 4.0, 4.0) .top_left_with_margins_on(state.ids.window_frame, 4.0, 4.0)
.set(state.ids.icon, ui); .set(state.ids.icon, ui);
// Close Button
// Close Button
if Button::image(self.imgs.close_button) if Button::image(self.imgs.close_button)
.w_h(24.0, 25.0) .w_h(24.0, 25.0)
.hover_image(self.imgs.close_button_hover) .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) .top_right_with_margins_on(state.ids.window, 74.0, 5.0)
.scroll_kids_vertically() .scroll_kids_vertically()
.set(state.ids.align_ing, ui); .set(state.ids.align_ing, ui);
// Category Tabs // 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| { state.update(|s| {
s.ids.category_bgs.resize( s.ids.category_bgs.resize(
SelectedCraftingTab::iter().enumerate().len(), CraftingTab::iter().enumerate().len(),
&mut ui.widget_id_generator(), &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| { state.update(|s| {
s.ids.category_tabs.resize( s.ids.category_tabs.resize(
SelectedCraftingTab::iter().enumerate().len(), CraftingTab::iter().enumerate().len(),
&mut ui.widget_id_generator(), &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| { state.update(|s| {
s.ids.category_imgs.resize( s.ids.category_imgs.resize(
SelectedCraftingTab::iter().enumerate().len(), CraftingTab::iter().enumerate().len(),
&mut ui.widget_id_generator(), &mut ui.widget_id_generator(),
) )
}) })
}; };
let sel_crafting_tab = &self.show.crafting_tab; let sel_crafting_tab = &self.show.crafting_tab;
for i in SelectedCraftingTab::iter().enumerate() { for (i, crafting_tab) in CraftingTab::iter().enumerate() {
// TODO: i18n! let tab_img = crafting_tab.img_id(self.imgs);
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,
};
// Button Background // Button Background
let mut bg = Image::new(self.imgs.pixel) let mut bg = Image::new(self.imgs.pixel)
.w_h(40.0, 30.0) .w_h(40.0, 30.0)
.color(Some(UI_MAIN)); .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) bg = bg.top_left_with_margins_on(state.ids.window_frame, 50.0, -40.0)
} else { } 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 // 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 self.imgs.wpn_icon_border_pressed
} else { } else {
self.imgs.wpn_icon_border self.imgs.wpn_icon_border
}) })
.wh_of(state.ids.category_bgs[i.0]) .wh_of(state.ids.category_bgs[i])
.middle_of(state.ids.category_bgs[i.0]) .middle_of(state.ids.category_bgs[i])
.hover_image(if i.1 == *sel_crafting_tab { .hover_image(if crafting_tab == *sel_crafting_tab {
self.imgs.wpn_icon_border_pressed self.imgs.wpn_icon_border_pressed
} else { } else {
self.imgs.wpn_icon_border_mo 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 self.imgs.wpn_icon_border_pressed
} else { } else {
self.imgs.wpn_icon_border_press self.imgs.wpn_icon_border_press
}) })
.with_tooltip( .with_tooltip(
self.tooltip_manager, self.tooltip_manager,
tab_name, &self.localized_strings.get(crafting_tab.name_key()),
"", "",
&tabs_tooltip, &tabs_tooltip,
TEXT_COLOR, TEXT_COLOR,
) )
.set(state.ids.category_tabs[i.0], ui) .set(state.ids.category_tabs[i], ui)
.was_clicked() .was_clicked()
{ {
events.push(Event::ChangeCraftingTab(i.1)) events.push(Event::ChangeCraftingTab(crafting_tab))
}; };
// Tab images // Tab images
Image::new(tab_img) 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) .w_h(20.0, 20.0)
.graphics_for(state.ids.category_tabs[i.0]) .graphics_for(state.ids.category_tabs[i])
.set(state.ids.category_imgs[i.0], ui); .set(state.ids.category_imgs[i], ui);
} }
let client = &self.client;
// First available recipes, then unavailable ones, each alphabetically // First available recipes, then unavailable ones, each alphabetically
// In the triples, "name" is the recipe book key, and "recipe.output.0.name()" // In the triples, "name" is the recipe book key, and "recipe.output.0.name()"
// is the display name (as stored in the item descriptors) // is the display name (as stored in the item descriptors)
@ -366,6 +414,17 @@ impl<'a> Widget for Crafting<'a> {
.client .client
.recipe_book() .recipe_book()
.iter() .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)| { .map(|(name, recipe)| {
let at_least_some_ingredients = recipe.inputs.iter().any(|(input, amount)| { let at_least_some_ingredients = recipe.inputs.iter().any(|(input, amount)| {
*amount > 0 *amount > 0
@ -375,7 +434,7 @@ impl<'a> Widget for Crafting<'a> {
.unwrap_or(false) .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 RecipeIngredientQuantity::All
} else if at_least_some_ingredients { } else if at_least_some_ingredients {
RecipeIngredientQuantity::Some RecipeIngredientQuantity::Some
@ -386,129 +445,19 @@ impl<'a> Widget for Crafting<'a> {
}) })
.collect(); .collect();
ordered_recipes.sort_by_key(|(_, recipe, state)| (*state, recipe.output.0.name())); 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 // 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 for (i, (name, recipe, quantity)) in ordered_recipes
.into_iter() .into_iter()
.filter(|(_name, recipe, _quantity)| match &self.show.crafting_tab { .filter(|(_, recipe, _)| self.show.crafting_tab.satisfies(recipe.output.0.as_ref()))
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,
},
})
.enumerate() .enumerate()
{ {
let button = Button::image( let button = Button::image(
@ -562,11 +511,11 @@ impl<'a> Widget for Crafting<'a> {
} }
} }
//Ingredients // Selected Recipe
if let Some(recipe) = state if let Some((recipe_name, recipe)) = state
.selected_recipe .selected_recipe
.as_ref() .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 // Title
Text::new(&recipe.output.0.name()) Text::new(&recipe.output.0.name())
@ -576,6 +525,132 @@ impl<'a> Widget for Crafting<'a> {
.color(TEXT_COLOR) .color(TEXT_COLOR)
.parent(state.ids.window) .parent(state.ids.window)
.set(state.ids.title_ing, ui); .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::<Vec<_>>()
.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 // Ingredient images with tooltip
if state.ids.ingredient_frame.len() < recipe.inputs().len() { if state.ids.ingredient_frame.len() < recipe.inputs().len() {
state.update(|state| { 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 // Scrollbars
Scrollbar::y_axis(state.ids.align_rec) Scrollbar::y_axis(state.ids.align_rec)
.thickness(5.0) .thickness(5.0)
@ -735,15 +860,6 @@ impl<'a> Widget for Crafting<'a> {
.rgba(0.33, 0.33, 0.33, 1.0) .rgba(0.33, 0.33, 0.33, 1.0)
.set(state.ids.scrollbar_ing, ui); .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 events
} }
} }

View File

@ -88,15 +88,6 @@ image_ids! {
// Social Window // Social Window
social_frame_on: "voxygen.element.misc_bg.social_frame", social_frame_on: "voxygen.element.misc_bg.social_frame",
social_bg_on: "voxygen.element.misc_bg.social_bg", 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
crafting_window: "voxygen.element.misc_bg.crafting", crafting_window: "voxygen.element.misc_bg.crafting",
@ -110,6 +101,7 @@ image_ids! {
icon_dismantle: "voxygen.element.icons.dismantle", icon_dismantle: "voxygen.element.icons.dismantle",
icon_food: "voxygen.element.icons.foods", icon_food: "voxygen.element.icons.foods",
icon_glider: "voxygen.element.icons.gliders", icon_glider: "voxygen.element.icons.gliders",
icon_globe: "voxygen.element.icons.globe",
icon_potion: "voxygen.element.icons.potions", icon_potion: "voxygen.element.icons.potions",
icon_utility: "voxygen.element.icons.utilities", icon_utility: "voxygen.element.icons.utilities",
icon_weapon: "voxygen.element.icons.weapons", icon_weapon: "voxygen.element.icons.weapons",
@ -403,6 +395,10 @@ image_ids! {
close_button_hover: "voxygen.element.buttons.close_btn_hover", close_button_hover: "voxygen.element.buttons.close_btn_hover",
close_button_press: "voxygen.element.buttons.close_btn_press", 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 // Inventory
collapse_btn: "voxygen.element.buttons.inv_collapse", collapse_btn: "voxygen.element.buttons.inv_collapse",
collapse_btn_hover: "voxygen.element.buttons.inv_collapse_hover", collapse_btn_hover: "voxygen.element.buttons.inv_collapse_hover",

View File

@ -31,7 +31,7 @@ use buffs::BuffsBar;
use buttons::Buttons; use buttons::Buttons;
use chat::Chat; use chat::Chat;
use chrono::NaiveTime; use chrono::NaiveTime;
use crafting::{Crafting, SelectedCraftingTab}; use crafting::{Crafting, CraftingTab};
use diary::{Diary, SelectedSkillTree}; use diary::{Diary, SelectedSkillTree};
use esc_menu::EscMenu; use esc_menu::EscMenu;
use group::Group; use group::Group;
@ -44,7 +44,7 @@ use prompt_dialog::PromptDialog;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings_window::{SettingsTab, SettingsWindow}; use settings_window::{SettingsTab, SettingsWindow};
use skillbar::Skillbar; use skillbar::Skillbar;
use social::{Social, SocialTab}; use social::Social;
use trade::Trade; use trade::Trade;
use crate::{ use crate::{
@ -546,8 +546,9 @@ pub struct Show {
ingame: bool, ingame: bool,
settings_tab: SettingsTab, settings_tab: SettingsTab,
skilltreetab: SelectedSkillTree, skilltreetab: SelectedSkillTree,
crafting_tab: SelectedCraftingTab, crafting_tab: CraftingTab,
social_tab: SocialTab, crafting_search_key: Option<String>,
social_search_key: Option<String>,
want_grab: bool, want_grab: bool,
stats: bool, stats: bool,
free_look: bool, free_look: bool,
@ -561,6 +562,10 @@ impl Show {
self.bag = open; self.bag = open;
self.map = false; self.map = false;
self.want_grab = !open; self.want_grab = !open;
if !open {
self.crafting = false;
}
} }
} }
@ -590,6 +595,10 @@ impl Show {
fn social(&mut self, open: bool) { fn social(&mut self, open: bool) {
if !self.esc_menu { if !self.esc_menu {
if !self.social && open {
// rising edge detector
self.search_social_players(None);
}
self.social = open; self.social = open;
self.diary = false; self.diary = false;
self.want_grab = !open; self.want_grab = !open;
@ -598,6 +607,10 @@ impl Show {
fn crafting(&mut self, open: bool) { fn crafting(&mut self, open: bool) {
if !self.esc_menu { if !self.esc_menu {
if !self.crafting && open {
// rising edge detector
self.search_crafting_recipe(None);
}
self.crafting = open; self.crafting = open;
self.bag = open; self.bag = open;
self.map = false; self.map = false;
@ -711,11 +724,6 @@ impl Show {
fn toggle_crafting(&mut self) { self.crafting(!self.crafting) } 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) { fn toggle_spell(&mut self) {
self.diary = !self.diary; self.diary = !self.diary;
self.bag = false; self.bag = false;
@ -730,8 +738,14 @@ impl Show {
self.social = false; self.social = false;
} }
fn selected_crafting_tab(&mut self, sel_cat: SelectedCraftingTab) { fn selected_crafting_tab(&mut self, sel_cat: CraftingTab) { self.crafting_tab = sel_cat; }
self.crafting_tab = sel_cat;
fn search_crafting_recipe(&mut self, search_key: Option<String>) {
self.crafting_search_key = search_key;
}
fn search_social_players(&mut self, search_key: Option<String>) {
self.social_search_key = search_key;
} }
/// If all of the menus are closed, adjusts coordinates of cursor to center /// If all of the menus are closed, adjusts coordinates of cursor to center
@ -890,8 +904,9 @@ impl Hud {
group_menu: false, group_menu: false,
settings_tab: SettingsTab::Interface, settings_tab: SettingsTab::Interface,
skilltreetab: SelectedSkillTree::General, skilltreetab: SelectedSkillTree::General,
crafting_tab: SelectedCraftingTab::Armor, crafting_tab: CraftingTab::All,
social_tab: SocialTab::Online, crafting_search_key: None,
social_search_key: None,
want_grab: true, want_grab: true,
ingame: true, ingame: true,
stats: false, stats: false,
@ -2368,7 +2383,6 @@ impl Hud {
Some(bag::Event::Close) => { Some(bag::Event::Close) => {
self.show.stats = false; self.show.stats = false;
self.show.bag(false); self.show.bag(false);
self.show.crafting(false);
if !self.show.social { if !self.show.social {
self.show.want_grab = true; self.show.want_grab = true;
self.force_ungrab = false; self.force_ungrab = false;
@ -2469,7 +2483,6 @@ impl Hud {
crafting::Event::Close => { crafting::Event::Close => {
self.show.stats = false; self.show.stats = false;
self.show.crafting(false); self.show.crafting(false);
self.show.bag(false);
if !self.show.social { if !self.show.social {
self.show.want_grab = true; self.show.want_grab = true;
self.force_ungrab = false; self.force_ungrab = false;
@ -2480,6 +2493,12 @@ impl Hud {
crafting::Event::ChangeCraftingTab(sel_cat) => { crafting::Event::ChangeCraftingTab(sel_cat) => {
self.show.selected_crafting_tab(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 self.force_ungrab = true
}; };
}, },
social::Event::ChangeSocialTab(social_tab) => { social::Event::Focus(widget_id) => {
self.show.open_social_tab(social_tab) self.to_focus = Some(Some(widget_id));
}, },
social::Event::Invite(uid) => events.push(Event::InviteMember(uid)), social::Event::Invite(uid) => events.push(Event::InviteMember(uid)),
social::Event::SearchPlayers(search_key) => {
self.show.search_social_players(search_key)
},
} }
} }
} }

View File

@ -11,7 +11,7 @@ use client::{self, Client};
use common::{comp::group, uid::Uid}; use common::{comp::group, uid::Uid};
use conrod_core::{ use conrod_core::{
color, 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, widget_ids, Color, Colorable, Labelable, Positionable, Sizeable, Widget, WidgetCommon,
}; };
use itertools::Itertools; use itertools::Itertools;
@ -27,25 +27,14 @@ widget_ids! {
icon, icon,
scrollbar, scrollbar,
online_align, online_align,
online_tab,
names_align,
name_txt,
player_levels[],
player_names[], player_names[],
player_zones[],
online_txt, online_txt,
online_no, 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, 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)>, selected_uid: Option<(Uid, Instant)>,
} }
pub enum SocialTab {
Online,
Friends,
Faction,
}
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct Social<'a> { pub struct Social<'a> {
show: &'a Show, show: &'a Show,
@ -106,7 +89,8 @@ impl<'a> Social<'a> {
pub enum Event { pub enum Event {
Close, Close,
Invite(Uid), Invite(Uid),
ChangeSocialTab(SocialTab), Focus(widget::Id),
SearchPlayers(Option<String>),
} }
impl<'a> Widget for Social<'a> { impl<'a> Widget for Social<'a> {
@ -145,33 +129,27 @@ impl<'a> Widget for Social<'a> {
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.desc_text_color(TEXT_COLOR); .desc_text_color(TEXT_COLOR);
// Window frame and BG // Window BG
let pos = if self.show.group || self.show.group_menu { Image::new(self.imgs.social_bg_on)
200.0 .bottom_left_with_margins_on(ui.window, 308.0, 25.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)
.color(Some(UI_MAIN)) .color(Some(UI_MAIN))
.w_h(280.0, 460.0) .w_h(280.0, 460.0)
.set(state.ids.bg, ui); .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) .middle_of(state.ids.bg)
.color(Some(UI_HIGHLIGHT_0)) .color(Some(UI_HIGHLIGHT_0))
.w_h(280.0, 460.0) .w_h(280.0, 460.0)
.set(state.ids.frame, ui); .set(state.ids.frame, ui);
// Icon // Icon
Image::new(self.imgs.social) Image::new(self.imgs.social)
.w_h(30.0, 30.0) .w_h(30.0, 30.0)
@ -200,381 +178,269 @@ impl<'a> Widget for Social<'a> {
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(state.ids.title, ui); .set(state.ids.title, ui);
// Tabs Buttons let players = self
// Online Tab Button .client
if Button::image(match &self.show.social_tab { .player_list()
SocialTab::Online => self.imgs.social_tab_online, .iter()
_ => self.imgs.social_tab_inact, .filter(|(_, p)| p.is_online);
}) let player_count = players.clone().count();
.w_h(30.0, 44.0)
.image_color(match &self.show.social_tab { // Content Alignment
SocialTab::Online => UI_MAIN, Rectangle::fill_with([270.0, 346.0], color::TRANSPARENT)
_ => Color::Rgba(1.0, 1.0, 1.0, 0.6), .mid_top_with_margin_on(state.ids.frame, 74.0)
}) .scroll_kids_vertically()
.top_right_with_margins_on(state.ids.frame, 50.0, -27.0) .set(state.ids.online_align, ui);
.set(state.ids.online_tab, ui) Scrollbar::y_axis(state.ids.online_align)
.was_clicked() .thickness(4.0)
{ .color(Color::Rgba(0.79, 1.09, 1.09, 0.0))
events.push(Event::ChangeSocialTab(SocialTab::Online)); .set(state.ids.scrollbar, ui);
}
// Friends Tab Button // Online Text
if Button::image(match &self.show.social_tab { Text::new(&self.localized_strings.get("hud.social.online"))
SocialTab::Friends => self.imgs.social_tab_act, .bottom_left_with_margins_on(state.ids.frame, 18.0, 10.0)
_ => self.imgs.social_tab_inact, .font_id(self.fonts.cyri.conrod_id)
}) .font_size(self.fonts.cyri.scale(14))
.w_h(30.0, 44.0) .color(TEXT_COLOR)
.hover_image(match &self.show.social_tab { .set(state.ids.online_txt, ui);
SocialTab::Friends => self.imgs.social_tab_act, Text::new(&player_count.to_string())
_ => self.imgs.social_tab_inact_hover, .right_from(state.ids.online_txt, 5.0)
}) .font_id(self.fonts.cyri.conrod_id)
.press_image(match &self.show.social_tab { .font_size(self.fonts.cyri.scale(14))
SocialTab::Friends => self.imgs.social_tab_act, .color(TEXT_COLOR)
_ => self.imgs.social_tab_inact_press, .set(state.ids.online_no, ui);
}) // Adjust widget_id struct vec length to player count
.down_from(state.ids.online_tab, 0.0) if state.ids.player_names.len() < player_count {
.image_color(match &self.show.social_tab { state.update(|s| {
SocialTab::Friends => UI_MAIN, s.ids
_ => Color::Rgba(1.0, 1.0, 1.0, 0.6), .player_names
}) .resize(player_count, &mut ui.widget_id_generator())
.set(state.ids.friends_tab, ui) })
.was_clicked() };
{
events.push(Event::ChangeSocialTab(SocialTab::Friends)); // Filter out yourself from the online list and perform search
} let my_uid = self.client.uid();
// Faction Tab Button let mut player_list = players
if Button::image(match &self.show.social_tab { .filter(|(uid, _)| Some(**uid) != my_uid)
SocialTab::Friends => self.imgs.social_tab_act, .filter(|(_, player)| {
_ => self.imgs.social_tab_inact, self.show
}) .social_search_key
.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
.as_ref() .as_ref()
.map(|character| character.name.to_lowercase()) .map(|search_key| {
.unwrap_or_else(|| player.player_alias.to_string()) search_key
}); .to_lowercase()
for (i, (&uid, player_info)) in player_list.into_iter().enumerate() { .split_whitespace()
let hide_username = true; .all(|substring| {
let zone = ""; // TODO Add real zone let player_alias = &player.player_alias.to_lowercase();
let selected = state.selected_uid.map_or(false, |u| u.0 == uid); let character_name = player
let alias = &player_info.player_alias; .character
let zone_name = match &player_info.character { .as_ref()
None => self.localized_strings.get("hud.group.in_menu").to_string(), /* character select or spectating */ .map(|character| character.name.to_lowercase());
_ => format!("{} ", &zone), player_alias.contains(substring)
}; || character_name
let name_text = match &player_info.character { .map(|cn| cn.contains(substring))
Some(character) => { .unwrap_or(false)
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
}) })
.press_image(if selected { .unwrap_or(true)
self.imgs.selection })
.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 { } else {
self.imgs.selection_press format!("[{}] {}", alias, &character.name)
}) }
.label(&name_text) },
.label_font_size(self.fonts.cyri.scale(14)) None => format!(
.label_y(conrod_core::position::Relative::Scalar(1.0)) "{} [{}]",
.label_font_id(self.fonts.cyri.conrod_id) alias.clone(),
.label_color(TEXT_COLOR) self.localized_strings.get("hud.group.in_menu").to_string()
.with_tooltip( ), // character select or spectating
self.tooltip_manager, };
&acc_name_txt, let acc_name_txt = format!(
"", "{}: {}",
&button_tooltip, &self.localized_strings.get("hud.social.account"),
TEXT_COLOR, alias
) );
.set(state.ids.player_names[i], ui); // Player name widget
// Player Zones let button = Button::image(if !selected {
Button::image(if !selected { self.imgs.nothing
self.imgs.nothing } else {
} else { self.imgs.selection
self.imgs.selection })
}) .hover_image(if selected {
.w_h(94.0, 20.0) self.imgs.selection
.right_from(state.ids.player_levels[i], 2.0) } else {
.label(&zone_name) 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_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_font_id(self.fonts.cyri.conrod_id)
.label_color(TEXT_COLOR) .label_color(TEXT_COLOR)
.label_y(conrod_core::position::Relative::Scalar(1.0)) .with_tooltip(
.parent(state.ids.zones_align) self.tooltip_manager,
.set(state.ids.player_zones[i], ui); &acc_name_txt,
// Check for click "",
if ui &button_tooltip,
.widget_input(state.ids.player_names[i]) TEXT_COLOR,
.clicks() )
.left() .set(state.ids.player_names[i], ui)
.next() .was_clicked()
.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()
{ {
if let Some(uid) = selected_to_invite { state.update(|s| s.selected_uid = Some((uid, Instant::now())));
events.push(Event::Invite(uid));
state.update(|s| {
s.selected_uid = None;
});
}
} }
} // 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 events
} }