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 4703a7e563
commit 1b3536a5f5
21 changed files with 641 additions and 627 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

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
}