Merge branch 'hqurve/crafting-ui' into 'master'

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

See merge request veloren/veloren!2081
This commit is contained in:
Ben Wallis 2021-04-07 20:24:23 +00:00
commit fd77966293
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
- 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

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.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",
},

View File

@ -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<String>),
}
#[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>) -> 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::<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
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
}
}

View File

@ -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",

View File

@ -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<String>,
social_search_key: Option<String>,
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<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
@ -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)
},
}
}
}

View File

@ -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<String>),
}
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
}