mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Add more parts of the character selection screen, add mouse detector widget, misc tweaks
This commit is contained in:
parent
de770694d8
commit
42c6550a0b
@ -108,6 +108,9 @@ Is the client up to date?"#,
|
||||
|
||||
|
||||
/// Start Main screen section
|
||||
"main.username": "Username",
|
||||
"main.server": "Server",
|
||||
"main.password": "Password",
|
||||
"main.connecting": "Connecting",
|
||||
"main.creating_world": "Creating world",
|
||||
"main.tip": "Tip:",
|
||||
@ -494,7 +497,7 @@ magically infused items?"#,
|
||||
"char_selection.accessories": "Accessories",
|
||||
"char_selection.create_info_name": "Your Character needs a name!",
|
||||
|
||||
/// End chracter selection section
|
||||
/// End character selection section
|
||||
|
||||
|
||||
/// Start character window section
|
||||
|
@ -37,13 +37,22 @@ impl CharSelectionState {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_humanoid_body(&self, client: &Client) -> Option<comp::humanoid::Body> {
|
||||
self.char_selection_ui
|
||||
.display_character(&client.character_list.characters)
|
||||
.and_then(|character| match character.body {
|
||||
comp::Body::Humanoid(body) => Some(body),
|
||||
_ => None,
|
||||
fn get_humanoid_body_loadout<'a>(
|
||||
char_selection_ui: &'a CharSelectionUi,
|
||||
client: &'a Client,
|
||||
) -> (Option<comp::humanoid::Body>, Option<&'a comp::Loadout>) {
|
||||
char_selection_ui
|
||||
.display_body_loadout(&client.character_list.characters)
|
||||
.map(|(body, loadout)| {
|
||||
(
|
||||
match body {
|
||||
comp::Body::Humanoid(body) => Some(body),
|
||||
_ => None,
|
||||
},
|
||||
Some(loadout),
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +96,9 @@ impl PlayState for CharSelectionState {
|
||||
return PlayStateResult::Pop;
|
||||
},
|
||||
ui::Event::AddCharacter { alias, tool, body } => {
|
||||
self.client.borrow_mut().create_character(alias, tool, body);
|
||||
self.client
|
||||
.borrow_mut()
|
||||
.create_character(alias, Some(tool), body);
|
||||
},
|
||||
ui::Event::DeleteCharacter(character_id) => {
|
||||
self.client.borrow_mut().delete_character(character_id);
|
||||
@ -103,13 +114,11 @@ impl PlayState for CharSelectionState {
|
||||
}
|
||||
}
|
||||
|
||||
let loadout = self.char_selection_ui.get_loadout();
|
||||
|
||||
// Maintain the scene.
|
||||
{
|
||||
let client = self.client.borrow();
|
||||
let humanoid_body = self.get_humanoid_body(&client);
|
||||
let loadout = self.char_selection_ui.get_loadout();
|
||||
let (humanoid_body, loadout) =
|
||||
Self::get_humanoid_body_loadout(&self.char_selection_ui, &client);
|
||||
|
||||
// Maintain the scene.
|
||||
let scene_data = scene::SceneData {
|
||||
@ -128,11 +137,8 @@ impl PlayState for CharSelectionState {
|
||||
as f32,
|
||||
};
|
||||
|
||||
self.scene.maintain(
|
||||
global_state.window.renderer_mut(),
|
||||
scene_data,
|
||||
loadout.as_ref(),
|
||||
);
|
||||
self.scene
|
||||
.maintain(global_state.window.renderer_mut(), scene_data, loadout);
|
||||
}
|
||||
|
||||
// Tick the client (currently only to keep the connection alive).
|
||||
@ -187,12 +193,12 @@ impl PlayState for CharSelectionState {
|
||||
|
||||
fn render(&mut self, renderer: &mut Renderer, _: &Settings) {
|
||||
let client = self.client.borrow();
|
||||
let humanoid_body = self.get_humanoid_body(&client);
|
||||
let loadout = self.char_selection_ui.get_loadout();
|
||||
let (humanoid_body, loadout) =
|
||||
Self::get_humanoid_body_loadout(&self.char_selection_ui, &client);
|
||||
|
||||
// Render the scene.
|
||||
self.scene
|
||||
.render(renderer, client.get_tick(), humanoid_body, loadout.as_ref());
|
||||
.render(renderer, client.get_tick(), humanoid_body, loadout);
|
||||
|
||||
// Draw the UI to the screen.
|
||||
self.char_selection_ui.render(renderer);
|
||||
|
@ -7,8 +7,11 @@ use crate::{
|
||||
ice::{
|
||||
component::neat_button,
|
||||
style,
|
||||
widget::{AspectRatioContainer, Overlay},
|
||||
Element, IcedUi as Ui,
|
||||
widget::{
|
||||
mouse_detector, AspectRatioContainer, BackgroundContainer, Image, MouseDetector,
|
||||
Overlay, Padding,
|
||||
},
|
||||
Element, IcedRenderer, IcedUi as Ui,
|
||||
},
|
||||
img_ids::{ImageGraphic, VoxelGraphic},
|
||||
},
|
||||
@ -19,6 +22,7 @@ use common::{
|
||||
assets::Asset,
|
||||
character::{CharacterId, CharacterItem, MAX_CHARACTERS_PER_PLAYER},
|
||||
comp::{self, humanoid},
|
||||
LoadoutBuilder,
|
||||
};
|
||||
//ImageFrame, Tooltip,
|
||||
use crate::settings::Settings;
|
||||
@ -26,7 +30,7 @@ use crate::settings::Settings;
|
||||
//use ui::ice::widget;
|
||||
use iced::{
|
||||
button, scrollable, text_input, Align, Button, Column, Container, HorizontalAlignment, Length,
|
||||
Row, Scrollable, Space, Text,
|
||||
Row, Scrollable, Space, Text, TextInput,
|
||||
};
|
||||
use vek::Rgba;
|
||||
|
||||
@ -40,17 +44,17 @@ const STARTER_BOW: &str = "common.items.weapons.bow.starter_bow";
|
||||
const STARTER_AXE: &str = "common.items.weapons.axe.starter_axe";
|
||||
const STARTER_STAFF: &str = "common.items.weapons.staff.starter_staff";
|
||||
const STARTER_SWORD: &str = "common.items.weapons.sword.starter_sword";
|
||||
const STARTER_DAGGER: &str = "common.items.weapons.dagger.starter_dagger";
|
||||
|
||||
// TODO: look into what was using this in old ui
|
||||
const UI_MAIN: iced::Color = iced::Color::from_rgba(0.61, 0.70, 0.70, 1.0); // Greenish Blue
|
||||
|
||||
image_ids_ice! {
|
||||
struct Imgs {
|
||||
<VoxelGraphic>
|
||||
<ImageGraphic>
|
||||
slider_range: "voxygen.element.slider.track",
|
||||
slider_indicator: "voxygen.element.slider.indicator",
|
||||
|
||||
<ImageGraphic>
|
||||
gray_corner: "voxygen.element.frames.gray.corner",
|
||||
gray_edge: "voxygen.element.frames.gray.edge",
|
||||
|
||||
@ -73,8 +77,6 @@ image_ids_ice! {
|
||||
staff: "voxygen.element.icons.staff",
|
||||
|
||||
// Species Icons
|
||||
male: "voxygen.element.icons.male",
|
||||
female: "voxygen.element.icons.female",
|
||||
human_m: "voxygen.element.icons.human_m",
|
||||
human_f: "voxygen.element.icons.human_f",
|
||||
orc_m: "voxygen.element.icons.orc_m",
|
||||
@ -115,7 +117,7 @@ pub enum Event {
|
||||
Play(CharacterId),
|
||||
AddCharacter {
|
||||
alias: String,
|
||||
tool: Option<String>,
|
||||
tool: String,
|
||||
body: comp::Body,
|
||||
},
|
||||
DeleteCharacter(CharacterId),
|
||||
@ -123,6 +125,7 @@ pub enum Event {
|
||||
|
||||
enum Mode {
|
||||
Select {
|
||||
info_content: Option<InfoContent>,
|
||||
// Index of selected character
|
||||
selected: Option<usize>,
|
||||
|
||||
@ -132,13 +135,20 @@ enum Mode {
|
||||
logout_button: button::State,
|
||||
enter_world_button: button::State,
|
||||
change_server_button: button::State,
|
||||
yes_button: button::State,
|
||||
no_button: button::State,
|
||||
},
|
||||
Create {
|
||||
name: String, // TODO: default to username
|
||||
body: humanoid::Body,
|
||||
loadout: comp::Loadout,
|
||||
tool: Option<&'static str>,
|
||||
// TODO: does this need to be an option, never seems to be none
|
||||
tool: &'static str,
|
||||
|
||||
body_type_buttons: [button::State; 2],
|
||||
species_buttons: [button::State; 6],
|
||||
tool_buttons: [button::State; 6],
|
||||
scroll: scrollable::State,
|
||||
name_input: text_input::State,
|
||||
back_button: button::State,
|
||||
create_button: button::State,
|
||||
@ -148,6 +158,7 @@ enum Mode {
|
||||
impl Mode {
|
||||
pub fn select() -> Self {
|
||||
Self::Select {
|
||||
info_content: None,
|
||||
selected: None,
|
||||
characters_scroll: Default::default(),
|
||||
character_buttons: Vec::new(),
|
||||
@ -155,16 +166,29 @@ impl Mode {
|
||||
logout_button: Default::default(),
|
||||
enter_world_button: Default::default(),
|
||||
change_server_button: Default::default(),
|
||||
yes_button: Default::default(),
|
||||
no_button: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create(name: String) -> Self {
|
||||
let tool = STARTER_SWORD;
|
||||
|
||||
let loadout = LoadoutBuilder::new()
|
||||
.defaults()
|
||||
.active_item(Some(LoadoutBuilder::default_item_config_from_str(tool)))
|
||||
.build();
|
||||
|
||||
Self::Create {
|
||||
name,
|
||||
body: humanoid::Body::random(),
|
||||
loadout: comp::Loadout::default(),
|
||||
tool: Some(STARTER_SWORD),
|
||||
loadout,
|
||||
tool,
|
||||
|
||||
body_type_buttons: Default::default(),
|
||||
species_buttons: Default::default(),
|
||||
tool_buttons: Default::default(),
|
||||
scroll: Default::default(),
|
||||
name_input: Default::default(),
|
||||
back_button: Default::default(),
|
||||
create_button: Default::default(),
|
||||
@ -204,7 +228,8 @@ struct Controls {
|
||||
// Alpha disclaimer
|
||||
alpha: String,
|
||||
|
||||
info_content: Option<InfoContent>,
|
||||
// Zone for rotating the character with the mouse
|
||||
mouse_detector: mouse_detector::State,
|
||||
// enter: bool,
|
||||
mode: Mode,
|
||||
}
|
||||
@ -214,11 +239,17 @@ enum Message {
|
||||
Back,
|
||||
Logout,
|
||||
EnterWorld,
|
||||
Select(usize),
|
||||
Delete(usize),
|
||||
ChangeServer,
|
||||
NewCharacter,
|
||||
CreateCharacter,
|
||||
Name(String),
|
||||
BodyType(humanoid::BodyType),
|
||||
Species(humanoid::Species),
|
||||
Tool(&'static str),
|
||||
CancelDeletion,
|
||||
ConfirmDeletion,
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
@ -237,14 +268,15 @@ impl Controls {
|
||||
version,
|
||||
alpha,
|
||||
|
||||
info_content: None,
|
||||
mouse_detector: Default::default(),
|
||||
mode: Mode::select(),
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&mut self, settings: &Settings, client: &Client) -> Element<Message> {
|
||||
// TODO: if enter key pressed and character is selected then enter the world
|
||||
// TODO: tooltip widget
|
||||
// TODO: use font scale thing for text size (use on button size for buttons with
|
||||
// text) TODO: if enter key pressed and character is selected then enter
|
||||
// the world TODO: tooltip widget
|
||||
|
||||
let imgs = &self.imgs;
|
||||
let fonts = &self.fonts;
|
||||
@ -275,6 +307,7 @@ impl Controls {
|
||||
|
||||
let content = match &mut self.mode {
|
||||
Mode::Select {
|
||||
info_content,
|
||||
selected,
|
||||
ref mut characters_scroll,
|
||||
ref mut character_buttons,
|
||||
@ -282,14 +315,14 @@ impl Controls {
|
||||
ref mut logout_button,
|
||||
ref mut enter_world_button,
|
||||
ref mut change_server_button,
|
||||
ref mut yes_button,
|
||||
ref mut no_button,
|
||||
} => {
|
||||
// TODO: impl delete prompt as overlay
|
||||
let server = Container::new(
|
||||
Column::with_children(vec![
|
||||
Text::new(&client.server_info.name)
|
||||
.size(fonts.cyri.scale(25))
|
||||
//.horizontal_alignment(HorizontalAlignment::Center)
|
||||
.into(),
|
||||
.size(fonts.cyri.scale(25))
|
||||
.into(),
|
||||
Container::new(neat_button(
|
||||
change_server_button,
|
||||
i18n.get("char_selection.change_server"),
|
||||
@ -333,18 +366,19 @@ impl Controls {
|
||||
Overlay::new(
|
||||
Button::new(
|
||||
select_button,
|
||||
Space::new(Length::Units(20), Length::Units(20)),
|
||||
Space::new(Length::Units(16), Length::Units(16)),
|
||||
)
|
||||
.style(
|
||||
style::button::Style::new(imgs.delete_button)
|
||||
.hover_image(imgs.delete_button_hover)
|
||||
.press_image(imgs.delete_button_press),
|
||||
),
|
||||
)
|
||||
.on_press(Message::Delete(i)),
|
||||
AspectRatioContainer::new(
|
||||
Button::new(
|
||||
delete_button,
|
||||
Column::with_children(vec![
|
||||
Text::new("Hi").width(Length::Fill).into(),
|
||||
Text::new("Hi").into(),
|
||||
Text::new("Hi").into(),
|
||||
Text::new("Hi").into(),
|
||||
]),
|
||||
@ -353,11 +387,14 @@ impl Controls {
|
||||
style::button::Style::new(imgs.selection)
|
||||
.hover_image(imgs.selection_hover)
|
||||
.press_image(imgs.selection_press),
|
||||
),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.on_press(Message::Select(i)),
|
||||
)
|
||||
.ratio_of_image(imgs.selection),
|
||||
)
|
||||
.padding(15)
|
||||
.padding(12)
|
||||
.align_x(Align::End)
|
||||
.into()
|
||||
})
|
||||
@ -393,7 +430,6 @@ impl Controls {
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
// TODO: try to get better interface for this in iced
|
||||
if num < MAX_CHARACTERS_PER_PLAYER {
|
||||
button.on_press(Message::NewCharacter)
|
||||
} else {
|
||||
@ -410,7 +446,7 @@ impl Controls {
|
||||
// children method
|
||||
let characters = Container::new(
|
||||
Scrollable::new(characters_scroll)
|
||||
.push(Column::with_children(characters).spacing(2)),
|
||||
.push(Column::with_children(characters).spacing(4)),
|
||||
)
|
||||
.style(style::container::Style::color_with_image_border(
|
||||
Rgba::new(0, 0, 0, 217),
|
||||
@ -424,14 +460,19 @@ impl Controls {
|
||||
let right_column = Column::with_children(vec![server.into(), characters.into()])
|
||||
.padding(15)
|
||||
.spacing(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.max_width(360);
|
||||
|
||||
let top = Container::new(right_column)
|
||||
.width(Length::Fill)
|
||||
.width(Length::Units(360)) // TODO: see if we can get iced to work with settings below
|
||||
//.max_width(360)
|
||||
//.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
let top = Row::with_children(vec![
|
||||
right_column.into(),
|
||||
MouseDetector::new(&mut self.mouse_detector, Length::Fill, Length::Fill).into(),
|
||||
])
|
||||
.padding(15)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
let logout = neat_button(
|
||||
logout_button,
|
||||
i18n.get("char_selection.logout"),
|
||||
@ -462,30 +503,359 @@ impl Controls {
|
||||
])
|
||||
.align_items(Align::End);
|
||||
|
||||
Column::with_children(vec![top.into(), bottom.into()])
|
||||
let content = Column::with_children(vec![top.into(), bottom.into()])
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
// Overlay delete prompt
|
||||
if let Some(info_content) = info_content {
|
||||
let over: Element<_> = match info_content {
|
||||
InfoContent::Deletion(_) => Container::new(
|
||||
Column::with_children(vec![
|
||||
Text::new(self.i18n.get("char_selection.delete_permanently"))
|
||||
.size(fonts.cyri.scale(24))
|
||||
.into(),
|
||||
Row::with_children(vec![
|
||||
neat_button(
|
||||
no_button,
|
||||
i18n.get("common.no"),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
Some(Message::CancelDeletion),
|
||||
),
|
||||
neat_button(
|
||||
yes_button,
|
||||
i18n.get("common.yes"),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
Some(Message::ConfirmDeletion),
|
||||
),
|
||||
])
|
||||
.height(Length::Units(28))
|
||||
.spacing(30)
|
||||
.into(),
|
||||
])
|
||||
.align_items(Align::Center)
|
||||
.spacing(10),
|
||||
)
|
||||
.style(
|
||||
style::container::Style::color_with_double_cornerless_border(
|
||||
(0, 0, 0, 200).into(),
|
||||
(3, 4, 4, 255).into(),
|
||||
(28, 28, 22, 255).into(),
|
||||
),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.max_width(400)
|
||||
.max_height(130)
|
||||
.padding(16)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.into(),
|
||||
// TODO
|
||||
_ => Space::new(Length::Shrink, Length::Shrink).into(),
|
||||
};
|
||||
|
||||
Overlay::new(over, content)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.into()
|
||||
} else {
|
||||
content.into()
|
||||
}
|
||||
},
|
||||
Mode::Create {
|
||||
name,
|
||||
body,
|
||||
loadout,
|
||||
tool,
|
||||
name_input,
|
||||
back_button,
|
||||
create_button,
|
||||
ref mut scroll,
|
||||
ref mut body_type_buttons,
|
||||
ref mut species_buttons,
|
||||
ref mut tool_buttons,
|
||||
ref mut name_input,
|
||||
ref mut back_button,
|
||||
ref mut create_button,
|
||||
} => {
|
||||
let top_row = Row::with_children(vec![]);
|
||||
let bottom_row = Row::with_children(vec![]);
|
||||
let unselected_style = style::button::Style::new(imgs.icon_border)
|
||||
.hover_image(imgs.icon_border_mo)
|
||||
.press_image(imgs.icon_border_press);
|
||||
|
||||
Column::with_children(vec![top_row.into(), bottom_row.into()])
|
||||
let selected_style = style::button::Style::new(imgs.icon_border_pressed)
|
||||
.hover_image(imgs.icon_border_mo)
|
||||
.press_image(imgs.icon_border_press);
|
||||
|
||||
let icon_button = |button, selected, msg, img| {
|
||||
Container::new(
|
||||
Button::<_, IcedRenderer>::new(
|
||||
button,
|
||||
Space::new(Length::Units(70), Length::Units(70)),
|
||||
)
|
||||
.style(if selected {
|
||||
selected_style
|
||||
} else {
|
||||
unselected_style
|
||||
})
|
||||
.on_press(msg),
|
||||
)
|
||||
.style(style::container::Style::image(img))
|
||||
};
|
||||
|
||||
let (body_m_ico, body_f_ico) = match body.species {
|
||||
humanoid::Species::Human => (imgs.human_m, imgs.human_f),
|
||||
humanoid::Species::Orc => (imgs.orc_m, imgs.orc_f),
|
||||
humanoid::Species::Dwarf => (imgs.dwarf_m, imgs.dwarf_f),
|
||||
humanoid::Species::Elf => (imgs.elf_m, imgs.elf_f),
|
||||
humanoid::Species::Undead => (imgs.undead_m, imgs.undead_f),
|
||||
humanoid::Species::Danari => (imgs.danari_m, imgs.danari_f),
|
||||
};
|
||||
|
||||
let [ref mut body_m_button, ref mut body_f_button] = body_type_buttons;
|
||||
let body_type = Row::with_children(vec![
|
||||
icon_button(
|
||||
body_m_button,
|
||||
matches!(body.body_type, humanoid::BodyType::Male),
|
||||
Message::BodyType(humanoid::BodyType::Male),
|
||||
body_m_ico,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
body_f_button,
|
||||
matches!(body.body_type, humanoid::BodyType::Female),
|
||||
Message::BodyType(humanoid::BodyType::Female),
|
||||
body_f_ico,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1);
|
||||
|
||||
let (human_icon, orc_icon, dwarf_icon, elf_icon, undead_icon, danari_icon) =
|
||||
match body.body_type {
|
||||
humanoid::BodyType::Male => (
|
||||
self.imgs.human_m,
|
||||
self.imgs.orc_m,
|
||||
self.imgs.dwarf_m,
|
||||
self.imgs.elf_m,
|
||||
self.imgs.undead_m,
|
||||
self.imgs.danari_m,
|
||||
),
|
||||
humanoid::BodyType::Female => (
|
||||
self.imgs.human_f,
|
||||
self.imgs.orc_f,
|
||||
self.imgs.dwarf_f,
|
||||
self.imgs.elf_f,
|
||||
self.imgs.undead_f,
|
||||
self.imgs.danari_f,
|
||||
),
|
||||
};
|
||||
|
||||
// TODO: tooltips
|
||||
let [ref mut human_button, ref mut orc_button, ref mut dwarf_button, ref mut elf_button, ref mut undead_button, ref mut danari_button] =
|
||||
species_buttons;
|
||||
let species = Column::with_children(vec![
|
||||
Row::with_children(vec![
|
||||
icon_button(
|
||||
human_button,
|
||||
matches!(body.species, humanoid::Species::Human),
|
||||
Message::Species(humanoid::Species::Human),
|
||||
human_icon,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
orc_button,
|
||||
matches!(body.species, humanoid::Species::Orc),
|
||||
Message::Species(humanoid::Species::Orc),
|
||||
orc_icon,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
dwarf_button,
|
||||
matches!(body.species, humanoid::Species::Dwarf),
|
||||
Message::Species(humanoid::Species::Dwarf),
|
||||
dwarf_icon,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
Row::with_children(vec![
|
||||
icon_button(
|
||||
elf_button,
|
||||
matches!(body.species, humanoid::Species::Elf),
|
||||
Message::Species(humanoid::Species::Elf),
|
||||
elf_icon,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
undead_button,
|
||||
matches!(body.species, humanoid::Species::Undead),
|
||||
Message::Species(humanoid::Species::Undead),
|
||||
undead_icon,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
danari_button,
|
||||
matches!(body.species, humanoid::Species::Danari),
|
||||
Message::Species(humanoid::Species::Danari),
|
||||
danari_icon,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1);
|
||||
|
||||
let [ref mut sword_button, ref mut daggers_button, ref mut axe_button, ref mut hammer_button, ref mut bow_button, ref mut staff_button] =
|
||||
tool_buttons;
|
||||
let tool = Column::with_children(vec![
|
||||
Row::with_children(vec![
|
||||
icon_button(
|
||||
sword_button,
|
||||
*tool == STARTER_SWORD,
|
||||
Message::Tool(STARTER_SWORD),
|
||||
imgs.sword,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
daggers_button,
|
||||
*tool == STARTER_DAGGER,
|
||||
Message::Tool(STARTER_DAGGER),
|
||||
imgs.daggers,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
axe_button,
|
||||
*tool == STARTER_AXE,
|
||||
Message::Tool(STARTER_AXE),
|
||||
imgs.axe,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
Row::with_children(vec![
|
||||
icon_button(
|
||||
hammer_button,
|
||||
*tool == STARTER_HAMMER,
|
||||
Message::Tool(STARTER_HAMMER),
|
||||
imgs.hammer,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
bow_button,
|
||||
*tool == STARTER_BOW,
|
||||
Message::Tool(STARTER_BOW),
|
||||
imgs.bow,
|
||||
)
|
||||
.into(),
|
||||
icon_button(
|
||||
staff_button,
|
||||
*tool == STARTER_STAFF,
|
||||
Message::Tool(STARTER_STAFF),
|
||||
imgs.staff,
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1)
|
||||
.into(),
|
||||
])
|
||||
.spacing(1);
|
||||
|
||||
let column_content = vec![body_type.into(), species.into(), tool.into()];
|
||||
|
||||
let right_column = Container::new(
|
||||
Column::with_children(vec![
|
||||
Text::new(i18n.get("char_selection.character_creation"))
|
||||
.size(fonts.cyri.scale(26))
|
||||
.into(),
|
||||
Scrollable::new(scroll)
|
||||
.push(
|
||||
Column::with_children(column_content)
|
||||
.align_items(Align::Center)
|
||||
.spacing(16),
|
||||
)
|
||||
.into(),
|
||||
])
|
||||
.spacing(20)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.align_items(Align::Center),
|
||||
)
|
||||
.style(style::container::Style::color_with_image_border(
|
||||
Rgba::new(0, 0, 0, 217),
|
||||
imgs.gray_corner,
|
||||
imgs.gray_edge,
|
||||
))
|
||||
.padding(10)
|
||||
.width(Length::Units(360)) // TODO: see if we can get iced to work with settings below
|
||||
//.max_width(360)
|
||||
//.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
let top = Row::with_children(vec![
|
||||
right_column.into(),
|
||||
MouseDetector::new(&mut self.mouse_detector, Length::Fill, Length::Fill).into(),
|
||||
])
|
||||
.padding(15)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
let back = neat_button(
|
||||
back_button,
|
||||
i18n.get("common.back"),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
Some(Message::Back),
|
||||
);
|
||||
|
||||
let name_input = BackgroundContainer::new(
|
||||
Image::new(imgs.name_input)
|
||||
.height(Length::Units(40))
|
||||
.fix_aspect_ratio(),
|
||||
TextInput::new(name_input, "Character Name", &name, Message::Name)
|
||||
.size(25)
|
||||
.on_submit(Message::CreateCharacter),
|
||||
)
|
||||
.padding(Padding::new().horizontal(7).top(5));
|
||||
|
||||
let create = neat_button(
|
||||
create_button,
|
||||
i18n.get("common.create"),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
(!name.is_empty()).then_some(Message::CreateCharacter),
|
||||
);
|
||||
|
||||
let bottom = Row::with_children(vec![
|
||||
Container::new(back)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Units(40))
|
||||
.into(),
|
||||
Container::new(name_input)
|
||||
.width(Length::Fill)
|
||||
.center_x()
|
||||
.into(),
|
||||
Container::new(create)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Units(40))
|
||||
.align_x(Align::End)
|
||||
.into(),
|
||||
])
|
||||
.align_items(Align::End);
|
||||
|
||||
Column::with_children(vec![top.into(), bottom.into()])
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
},
|
||||
};
|
||||
|
||||
Container::new(
|
||||
Column::with_children(vec![top_text.into(), content.into()])
|
||||
Column::with_children(vec![top_text.into(), content])
|
||||
.spacing(3)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
@ -494,15 +864,7 @@ impl Controls {
|
||||
.into()
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
events: &mut Vec<Event>,
|
||||
settings: &Settings,
|
||||
characters: &[CharacterItem],
|
||||
) {
|
||||
let servers = &settings.networking.servers;
|
||||
|
||||
fn update(&mut self, message: Message, events: &mut Vec<Event>, characters: &[CharacterItem]) {
|
||||
match message {
|
||||
Message::Back => {
|
||||
if matches!(&self.mode, Mode::Create { .. }) {
|
||||
@ -518,15 +880,20 @@ impl Controls {
|
||||
..
|
||||
} = &self.mode
|
||||
{
|
||||
// TODO: eliminate option in character id
|
||||
// TODO: eliminate option in character id?
|
||||
if let Some(id) = characters.get(*selected).and_then(|i| i.character.id) {
|
||||
events.push(Event::Play(id));
|
||||
}
|
||||
}
|
||||
},
|
||||
Message::Select(idx) => {
|
||||
if let Mode::Select { selected, .. } = &mut self.mode {
|
||||
*selected = Some(idx);
|
||||
}
|
||||
},
|
||||
Message::Delete(idx) => {
|
||||
if let Some(id) = characters.get(idx).and_then(|i| i.character.id) {
|
||||
events.push(Event::DeleteCharacter(id));
|
||||
if let Mode::Select { info_content, .. } = &mut self.mode {
|
||||
*info_content = Some(InfoContent::Deletion(idx));
|
||||
}
|
||||
},
|
||||
Message::ChangeServer => {
|
||||
@ -544,7 +911,7 @@ impl Controls {
|
||||
{
|
||||
events.push(Event::AddCharacter {
|
||||
alias: name.clone(),
|
||||
tool: tool.map(String::from),
|
||||
tool: String::from(*tool),
|
||||
body: comp::Body::Humanoid(*body),
|
||||
});
|
||||
self.mode = Mode::select();
|
||||
@ -555,16 +922,54 @@ impl Controls {
|
||||
*name = value;
|
||||
}
|
||||
},
|
||||
Message::BodyType(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
body.body_type = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::Species(value) => {
|
||||
if let Mode::Create { body, .. } = &mut self.mode {
|
||||
body.species = value;
|
||||
body.validate();
|
||||
}
|
||||
},
|
||||
Message::Tool(value) => {
|
||||
if let Mode::Create { tool, loadout, .. } = &mut self.mode {
|
||||
*tool = value;
|
||||
loadout.active_item = Some(LoadoutBuilder::default_item_config_from_str(*tool));
|
||||
}
|
||||
},
|
||||
Message::ConfirmDeletion => {
|
||||
if let Mode::Select { info_content, .. } = &mut self.mode {
|
||||
if let Some(InfoContent::Deletion(idx)) = info_content {
|
||||
if let Some(id) = characters.get(*idx).and_then(|i| i.character.id) {
|
||||
events.push(Event::DeleteCharacter(id));
|
||||
}
|
||||
*info_content = None;
|
||||
}
|
||||
}
|
||||
},
|
||||
Message::CancelDeletion => {
|
||||
if let Mode::Select { info_content, .. } = &mut self.mode {
|
||||
if let Some(InfoContent::Deletion(idx)) = info_content {
|
||||
*info_content = None;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the character to display
|
||||
pub fn display_character(&self, characters: &[CharacterItem]) -> Option<&CharacterItem> {
|
||||
match self.mode {
|
||||
// TODO
|
||||
Mode::Select { .. } => None,
|
||||
// TODO
|
||||
Mode::Create { .. } => None,
|
||||
pub fn display_body_loadout<'a>(
|
||||
&'a self,
|
||||
characters: &'a [CharacterItem],
|
||||
) -> Option<(comp::Body, &'a comp::Loadout)> {
|
||||
match &self.mode {
|
||||
Mode::Select { selected, .. } => selected
|
||||
.and_then(|idx| characters.get(idx))
|
||||
.map(|i| (i.body, &i.loadout)),
|
||||
Mode::Create { loadout, body, .. } => Some((comp::Body::Humanoid(*body), loadout)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -607,44 +1012,11 @@ impl CharSelectionUi {
|
||||
Self { ui, controls }
|
||||
}
|
||||
|
||||
pub fn display_character(&self, characters: &[CharacterItem]) -> Option<&CharacterItem> {
|
||||
self.controls.display_character(characters)
|
||||
}
|
||||
|
||||
// TODO
|
||||
pub fn get_loadout(&mut self) -> Option<comp::Loadout> {
|
||||
// TODO: error gracefully
|
||||
// TODO: don't clone
|
||||
/*match &mut self.mode {
|
||||
Mode::Select(character_list) => {
|
||||
if let Some(data) = character_list {
|
||||
data.get(self.selected_character).map(|c| c.loadout.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
Mode::Create { loadout, tool, .. } => {
|
||||
loadout.active_item = tool.map(|tool| comp::ItemConfig {
|
||||
item: comp::Item::new_from_asset_expect(tool),
|
||||
ability1: None,
|
||||
ability2: None,
|
||||
ability3: None,
|
||||
block_ability: None,
|
||||
dodge_ability: None,
|
||||
});
|
||||
loadout.chest = Some(comp::Item::new_from_asset_expect(
|
||||
"common.items.armor.starter.rugged_chest",
|
||||
));
|
||||
loadout.pants = Some(comp::Item::new_from_asset_expect(
|
||||
"common.items.armor.starter.rugged_pants",
|
||||
));
|
||||
loadout.foot = Some(comp::Item::new_from_asset_expect(
|
||||
"common.items.armor.starter.sandals_0",
|
||||
));
|
||||
Some(loadout.clone())
|
||||
},
|
||||
}*/
|
||||
None
|
||||
pub fn display_body_loadout<'a>(
|
||||
&'a self,
|
||||
characters: &'a [CharacterItem],
|
||||
) -> Option<(comp::Body, &'a comp::Loadout)> {
|
||||
self.controls.display_body_loadout(characters)
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: window::Event) -> bool {
|
||||
@ -654,15 +1026,13 @@ impl CharSelectionUi {
|
||||
true
|
||||
},
|
||||
window::Event::MouseButton(_, window::PressState::Pressed) => {
|
||||
// TODO: implement this with iced
|
||||
// !self.ui.no_widget_capturing_mouse()
|
||||
false
|
||||
!self.controls.mouse_detector.mouse_over()
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: do we need whole client here or just character list
|
||||
// TODO: do we need whole client here or just character list?
|
||||
pub fn maintain(&mut self, global_state: &mut GlobalState, client: &mut Client) -> Vec<Event> {
|
||||
let mut events = Vec::new();
|
||||
|
||||
@ -672,17 +1042,13 @@ impl CharSelectionUi {
|
||||
);
|
||||
|
||||
messages.into_iter().for_each(|message| {
|
||||
self.controls.update(
|
||||
message,
|
||||
&mut events,
|
||||
&global_state.settings,
|
||||
&client.character_list.characters,
|
||||
)
|
||||
self.controls
|
||||
.update(message, &mut events, &client.character_list.characters)
|
||||
});
|
||||
|
||||
events
|
||||
}
|
||||
|
||||
// TODO: do we need globals
|
||||
// TODO: do we need globals?
|
||||
pub fn render(&self, renderer: &mut Renderer) { self.ui.render(renderer); }
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
};
|
||||
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
|
||||
use common::{assets::Asset, comp, span};
|
||||
use tracing::{error, warn};
|
||||
use tracing::error;
|
||||
use ui::{Event as MainMenuEvent, MainMenuUi};
|
||||
|
||||
pub struct MainMenuState {
|
||||
@ -221,12 +221,11 @@ impl PlayState for MainMenuState {
|
||||
} => {
|
||||
let mut net_settings = &mut global_state.settings.networking;
|
||||
net_settings.username = username.clone();
|
||||
net_settings.default_server = server_address.clone();
|
||||
if !net_settings.servers.contains(&server_address) {
|
||||
net_settings.servers.push(server_address.clone());
|
||||
}
|
||||
if let Err(err) = global_state.settings.save_to_file() {
|
||||
warn!("Failed to save settings: {:?}", err);
|
||||
}
|
||||
global_state.settings.save_to_file_warn();
|
||||
|
||||
attempt_login(
|
||||
&mut global_state.settings,
|
||||
|
@ -202,6 +202,7 @@ impl Banner {
|
||||
.padding(10)
|
||||
.height(Length::FillPortion(25))
|
||||
.into(),
|
||||
// TODO: i18n
|
||||
Column::with_children(vec![
|
||||
BackgroundContainer::new(
|
||||
Image::new(imgs.input_bg)
|
||||
@ -209,7 +210,7 @@ impl Banner {
|
||||
.fix_aspect_ratio(),
|
||||
TextInput::new(
|
||||
&mut self.username,
|
||||
"Username",
|
||||
i18n.get("main.username"),
|
||||
&login_info.username,
|
||||
Message::Username,
|
||||
)
|
||||
@ -224,7 +225,7 @@ impl Banner {
|
||||
.fix_aspect_ratio(),
|
||||
TextInput::new(
|
||||
&mut self.password,
|
||||
"Password",
|
||||
i18n.get("main.password"),
|
||||
&login_info.password,
|
||||
Message::Password,
|
||||
)
|
||||
@ -240,7 +241,7 @@ impl Banner {
|
||||
.fix_aspect_ratio(),
|
||||
TextInput::new(
|
||||
&mut self.server,
|
||||
"Server",
|
||||
i18n.get("main.server"),
|
||||
&login_info.server,
|
||||
Message::Server,
|
||||
)
|
||||
|
@ -266,7 +266,8 @@ impl IcedRenderer {
|
||||
let mesh_index = *mesh_index;
|
||||
let linear_color = *linear_color;
|
||||
// Could potentially pass this in as part of the extras
|
||||
let offset = offset.map(|e| e as f32 * p_scale) / half_res;
|
||||
let offset =
|
||||
offset.map(|e| e as f32 * p_scale) / half_res * Vec2::new(-1.0, 1.0);
|
||||
(0..*glyph_count).map(move |i| (mesh_index + i * 6, linear_color, offset))
|
||||
})
|
||||
.zip(self.last_glyph_verts.iter())
|
||||
@ -371,6 +372,68 @@ impl IcedRenderer {
|
||||
Aabr { min, max }
|
||||
}
|
||||
|
||||
fn position_glyphs(
|
||||
&mut self,
|
||||
bounds: iced::Rectangle,
|
||||
horizontal_alignment: iced::HorizontalAlignment,
|
||||
vertical_alignment: iced::VerticalAlignment,
|
||||
text: &str,
|
||||
size: u16,
|
||||
font: FontId,
|
||||
) -> Vec<glyph_brush::SectionGlyph> {
|
||||
use glyph_brush::{GlyphCruncher, HorizontalAlign, VerticalAlign};
|
||||
// TODO: add option to align based on the geometry of the rendered glyphs
|
||||
// instead of all possible glyphs
|
||||
let (x, h_align) = match horizontal_alignment {
|
||||
iced::HorizontalAlignment::Left => (bounds.x, HorizontalAlign::Left),
|
||||
iced::HorizontalAlignment::Center => (bounds.center_x(), HorizontalAlign::Center),
|
||||
iced::HorizontalAlignment::Right => (bounds.x + bounds.width, HorizontalAlign::Right),
|
||||
};
|
||||
|
||||
let (y, v_align) = match vertical_alignment {
|
||||
iced::VerticalAlignment::Top => (bounds.y, VerticalAlign::Top),
|
||||
iced::VerticalAlignment::Center => (bounds.center_y(), VerticalAlign::Center),
|
||||
iced::VerticalAlignment::Bottom => (bounds.y + bounds.height, VerticalAlign::Bottom),
|
||||
};
|
||||
|
||||
let p_scale = self.p_scale;
|
||||
|
||||
let section = glyph_brush::Section {
|
||||
screen_position: (x * p_scale, y * p_scale),
|
||||
bounds: (bounds.width * p_scale, bounds.height * p_scale),
|
||||
layout: glyph_brush::Layout::Wrap {
|
||||
line_breaker: Default::default(),
|
||||
h_align,
|
||||
v_align,
|
||||
},
|
||||
text: vec![glyph_brush::Text {
|
||||
text,
|
||||
scale: (size as f32 * p_scale).into(),
|
||||
font_id: font.0,
|
||||
extra: (),
|
||||
}],
|
||||
};
|
||||
|
||||
self
|
||||
.cache
|
||||
.glyph_cache_mut()
|
||||
.glyphs(section)
|
||||
// We would still have to generate vertices for these even if they have no pixels
|
||||
// Note: this is somewhat hacky and could fail if there is a non-whitespace character
|
||||
// that is not visible (to solve this we could use the extra values in
|
||||
// queue_pre_positioned to keep track of which glyphs are actually returned by
|
||||
// proccess_queued)
|
||||
.filter(|g| {
|
||||
!text[g.byte_index..]
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap()
|
||||
.is_whitespace()
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn draw_primitive(&mut self, primitive: Primitive, offset: Vec2<u32>, renderer: &mut Renderer) {
|
||||
match primitive {
|
||||
Primitive::Group { primitives } => {
|
||||
@ -391,8 +454,8 @@ impl IcedRenderer {
|
||||
|
||||
let (graphic_id, rotation) = handle;
|
||||
let gl_aabr = self.gl_aabr(iced::Rectangle {
|
||||
x: bounds.x + offset.x as f32,
|
||||
y: bounds.y + offset.y as f32,
|
||||
x: bounds.x - offset.x as f32,
|
||||
y: bounds.y - offset.y as f32,
|
||||
..bounds
|
||||
});
|
||||
|
||||
@ -468,8 +531,8 @@ impl IcedRenderer {
|
||||
self.switch_state(State::Plain);
|
||||
|
||||
let gl_aabr = self.gl_aabr(iced::Rectangle {
|
||||
x: bounds.x + offset.x as f32,
|
||||
y: bounds.y + offset.y as f32,
|
||||
x: bounds.x - offset.x as f32,
|
||||
y: bounds.y - offset.y as f32,
|
||||
..bounds
|
||||
});
|
||||
|
||||
@ -497,8 +560,8 @@ impl IcedRenderer {
|
||||
self.switch_state(State::Plain);
|
||||
|
||||
let gl_aabr = self.gl_aabr(iced::Rectangle {
|
||||
x: bounds.x + offset.x as f32,
|
||||
y: bounds.y + offset.y as f32,
|
||||
x: bounds.x - offset.x as f32,
|
||||
y: bounds.y - offset.y as f32,
|
||||
..bounds
|
||||
});
|
||||
|
||||
|
@ -5,6 +5,7 @@ mod column;
|
||||
mod compound_graphic;
|
||||
mod container;
|
||||
mod image;
|
||||
mod mouse_detector;
|
||||
mod overlay;
|
||||
mod row;
|
||||
mod scrollable;
|
||||
|
9
voxygen/src/ui/ice/renderer/widget/mouse_detector.rs
Normal file
9
voxygen/src/ui/ice/renderer/widget/mouse_detector.rs
Normal file
@ -0,0 +1,9 @@
|
||||
use super::super::{super::widget::mouse_detector, IcedRenderer, Primitive};
|
||||
use iced::{mouse, Rectangle};
|
||||
|
||||
impl mouse_detector::Renderer for IcedRenderer {
|
||||
fn draw(&mut self, _bounds: Rectangle) -> Self::Output {
|
||||
// TODO: mouse interaction if in bounds??
|
||||
(Primitive::Nothing, mouse::Interaction::default())
|
||||
}
|
||||
}
|
@ -42,54 +42,14 @@ impl text::Renderer for IcedRenderer {
|
||||
horizontal_alignment: HorizontalAlignment,
|
||||
vertical_alignment: VerticalAlignment,
|
||||
) -> Self::Output {
|
||||
use glyph_brush::{HorizontalAlign, VerticalAlign};
|
||||
// glyph_brush thought it would be a great idea to change what the bounds and
|
||||
// position mean based on the alignment
|
||||
// TODO: add option to align based on the geometry of the rendered glyphs
|
||||
// instead of all possible glyphs
|
||||
let (x, h_align) = match horizontal_alignment {
|
||||
HorizontalAlignment::Left => (bounds.x, HorizontalAlign::Left),
|
||||
HorizontalAlignment::Center => (bounds.center_x(), HorizontalAlign::Center),
|
||||
HorizontalAlignment::Right => (bounds.x + bounds.width, HorizontalAlign::Right),
|
||||
};
|
||||
|
||||
let (y, v_align) = match vertical_alignment {
|
||||
VerticalAlignment::Top => (bounds.y, VerticalAlign::Top),
|
||||
VerticalAlignment::Center => (bounds.center_y(), VerticalAlign::Center),
|
||||
VerticalAlignment::Bottom => (bounds.y + bounds.height, VerticalAlign::Bottom),
|
||||
};
|
||||
|
||||
let p_scale = self.p_scale;
|
||||
|
||||
let section = glyph_brush::Section {
|
||||
screen_position: (x * p_scale, y * p_scale),
|
||||
bounds: (bounds.width * p_scale, bounds.height * p_scale),
|
||||
layout: glyph_brush::Layout::Wrap {
|
||||
line_breaker: Default::default(),
|
||||
h_align,
|
||||
v_align,
|
||||
},
|
||||
text: vec![glyph_brush::Text {
|
||||
text: content,
|
||||
scale: (size as f32 * p_scale).into(),
|
||||
font_id: font.0,
|
||||
extra: (),
|
||||
}],
|
||||
};
|
||||
|
||||
let glyphs = self
|
||||
.cache
|
||||
.glyph_cache_mut()
|
||||
.glyphs(section)
|
||||
.filter(|g| {
|
||||
!content[g.byte_index..]
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap()
|
||||
.is_whitespace()
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let glyphs = self.position_glyphs(
|
||||
bounds,
|
||||
horizontal_alignment,
|
||||
vertical_alignment,
|
||||
content,
|
||||
size,
|
||||
font,
|
||||
);
|
||||
|
||||
(
|
||||
Primitive::Text {
|
||||
|
@ -60,7 +60,7 @@ impl text_input::Renderer for IcedRenderer {
|
||||
font_id: font.0,
|
||||
extra: (),
|
||||
}],
|
||||
};
|
||||
i;
|
||||
let font = glyph_calculator.fonts()[font.0].as_scaled(size as f32);
|
||||
let space_id = font.glyph_id(' ');
|
||||
let x_id = font.glyph_id('x');
|
||||
@ -215,9 +215,22 @@ impl text_input::Renderer for IcedRenderer {
|
||||
};
|
||||
|
||||
let display_text = text.unwrap_or(if state.is_focused() { "" } else { placeholder });
|
||||
let section = glyph_brush::Section {
|
||||
// Note: clip offset is an integer so we don't have to worry about not accounting for
|
||||
// that here where the alignment of the glyphs with pixels affects rasterization
|
||||
// Note: clip offset is an integer so we don't have to worry about not
|
||||
// accounting for that here where the alignment of the glyphs with
|
||||
// pixels affects rasterization
|
||||
let glyphs = self.position_glyphs(
|
||||
Rectangle {
|
||||
width: 1000.0, // hacky
|
||||
..bounds
|
||||
},
|
||||
iced::HorizontalAlignment::Left,
|
||||
iced::VerticalAlignment::Center,
|
||||
display_text,
|
||||
size,
|
||||
font,
|
||||
);
|
||||
// TODO: delete if new arrangment behaves nicely
|
||||
/*let section = glyph_brush::Section {
|
||||
screen_position: (text_bounds.x * p_scale, text_bounds.center_y() * p_scale),
|
||||
bounds: (
|
||||
10000.0, /* text_bounds.width * p_scale */
|
||||
@ -234,26 +247,7 @@ impl text_input::Renderer for IcedRenderer {
|
||||
font_id: font.0,
|
||||
extra: (),
|
||||
}],
|
||||
};
|
||||
|
||||
let glyphs = self
|
||||
.cache
|
||||
.glyph_cache_mut()
|
||||
.glyphs(section)
|
||||
// We would still have to generate vertices for these even if they have no pixels
|
||||
// Note: this is somewhat hacky and could fail if there is a non-whitespace character
|
||||
// that is not visible (to solve this we could use the extra values in
|
||||
// queue_pre_positioned to keep track of which glyphs are actually returned by
|
||||
// proccess_queued)
|
||||
.filter(|g| {
|
||||
!display_text[g.byte_index..]
|
||||
.chars()
|
||||
.next()
|
||||
.unwrap()
|
||||
.is_whitespace()
|
||||
})
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
};*/
|
||||
|
||||
let text_primitive = Primitive::Text {
|
||||
glyphs,
|
||||
|
@ -1,4 +1,6 @@
|
||||
use iced::{layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, Widget};
|
||||
use iced::{
|
||||
layout, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size, Widget,
|
||||
};
|
||||
use std::{hash::Hash, u32};
|
||||
|
||||
// Note: it might be more efficient to make this generic over the content type?
|
||||
@ -70,17 +72,15 @@ impl<'a, M, R> Widget<M, R> for AspectRatioContainer<'a, M, R>
|
||||
where
|
||||
R: self::Renderer,
|
||||
{
|
||||
fn width(&self) -> Length { Length::Fill }
|
||||
fn width(&self) -> Length { Length::Shrink }
|
||||
|
||||
fn height(&self) -> Length { Length::Fill }
|
||||
fn height(&self) -> Length { Length::Shrink }
|
||||
|
||||
fn layout(&self, renderer: &R, limits: &layout::Limits) -> layout::Node {
|
||||
let limits = limits
|
||||
.loose()
|
||||
.max_width(self.max_width)
|
||||
.max_height(self.max_height)
|
||||
.width(self.width())
|
||||
.height(self.height());
|
||||
.max_height(self.max_height);
|
||||
|
||||
let aspect_ratio = match &self.aspect_ratio {
|
||||
AspectRatio::Image(handle) => {
|
||||
@ -107,7 +107,9 @@ where
|
||||
limits.max_height((max_width / aspect_ratio) as u32)
|
||||
};
|
||||
|
||||
let content = self.content.layout(renderer, &limits.loose());
|
||||
// Remove fill limits in case one of the parents was Shrink
|
||||
let limits = layout::Limits::new(Size::ZERO, limits.max());
|
||||
let content = self.content.layout(renderer, &limits);
|
||||
|
||||
layout::Node::with_children(limits.max(), vec![content])
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ pub mod background_container;
|
||||
pub mod compound_graphic;
|
||||
pub mod fill_text;
|
||||
pub mod image;
|
||||
pub mod mouse_detector;
|
||||
pub mod overlay;
|
||||
pub mod stack;
|
||||
|
||||
@ -11,5 +12,6 @@ pub use self::{
|
||||
background_container::{BackgroundContainer, Padding},
|
||||
fill_text::FillText,
|
||||
image::Image,
|
||||
mouse_detector::MouseDetector,
|
||||
overlay::Overlay,
|
||||
};
|
||||
|
112
voxygen/src/ui/ice/widget/mouse_detector.rs
Normal file
112
voxygen/src/ui/ice/widget/mouse_detector.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use iced::{
|
||||
layout, mouse, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,
|
||||
Widget,
|
||||
};
|
||||
use std::hash::Hash;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct State {
|
||||
mouse_over: bool,
|
||||
}
|
||||
impl State {
|
||||
pub fn mouse_over(&self) -> bool { self.mouse_over }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MouseDetector<'a> {
|
||||
width: Length,
|
||||
height: Length,
|
||||
//on_enter: M,
|
||||
//on_exit: M,
|
||||
state: &'a mut State,
|
||||
}
|
||||
|
||||
impl<'a> MouseDetector<'a> {
|
||||
pub fn new(
|
||||
state: &'a mut State,
|
||||
//on_enter: M,
|
||||
//on_exit: M,
|
||||
width: Length,
|
||||
height: Length,
|
||||
) -> Self {
|
||||
Self {
|
||||
state,
|
||||
//on_enter,
|
||||
//on_exit,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, M, R> Widget<M, R> for MouseDetector<'a>
|
||||
where
|
||||
R: self::Renderer,
|
||||
{
|
||||
fn width(&self) -> Length { self.width }
|
||||
|
||||
fn height(&self) -> Length { self.height }
|
||||
|
||||
fn layout(&self, _renderer: &R, limits: &layout::Limits) -> layout::Node {
|
||||
let limits = limits.width(self.width).height(self.height);
|
||||
|
||||
layout::Node::new(limits.resolve(Size::ZERO))
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
_cursor_position: Point,
|
||||
_messages: &mut Vec<M>,
|
||||
_renderer: &R,
|
||||
_clipboard: Option<&dyn Clipboard>,
|
||||
) {
|
||||
if let Event::Mouse(mouse::Event::CursorMoved { x, y }) = event {
|
||||
let bounds = layout.bounds();
|
||||
let mouse_over = x > bounds.x
|
||||
&& x < bounds.x + bounds.width
|
||||
&& y > bounds.y
|
||||
&& y < bounds.y + bounds.height;
|
||||
if mouse_over != self.state.mouse_over {
|
||||
self.state.mouse_over = mouse_over;
|
||||
|
||||
/*messages.push(if mouse_over {
|
||||
self.on_enter.clone()
|
||||
} else {
|
||||
self.on_exit.clone()
|
||||
});*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
_defaults: &R::Defaults,
|
||||
layout: Layout<'_>,
|
||||
_cursor_position: Point,
|
||||
) -> R::Output {
|
||||
renderer.draw(layout.bounds())
|
||||
}
|
||||
|
||||
fn hash_layout(&self, state: &mut Hasher) {
|
||||
struct Marker;
|
||||
std::any::TypeId::of::<Marker>().hash(state);
|
||||
|
||||
self.width.hash(state);
|
||||
self.height.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Renderer: iced::Renderer {
|
||||
fn draw(&mut self, bounds: Rectangle) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<'a, M, R> From<MouseDetector<'a>> for Element<'a, M, R>
|
||||
where
|
||||
R: self::Renderer,
|
||||
M: 'a,
|
||||
{
|
||||
fn from(mouse_detector: MouseDetector<'a>) -> Element<'a, M, R> { Element::new(mouse_detector) }
|
||||
}
|
@ -116,14 +116,14 @@ where
|
||||
let over_size = over.size();
|
||||
|
||||
let size = limits.resolve(Size {
|
||||
width: under_size.width.max(over_size.width),
|
||||
height: under_size.height.max(over_size.height),
|
||||
width: under_size.width.max(over_size.width + padding * 2.0),
|
||||
height: under_size.height.max(over_size.height + padding * 2.0),
|
||||
});
|
||||
|
||||
over.move_to(Point::new(padding, padding));
|
||||
over.align(self.horizontal_alignment, self.vertical_alignment, size);
|
||||
|
||||
layout::Node::with_children(size.pad(padding), vec![over, under])
|
||||
layout::Node::with_children(size, vec![over, under])
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
@ -135,9 +135,12 @@ where
|
||||
renderer: &R,
|
||||
clipboard: Option<&dyn Clipboard>,
|
||||
) {
|
||||
let mut children = layout.children();
|
||||
let over_layout = children.next().unwrap();
|
||||
|
||||
self.over.on_event(
|
||||
event.clone(),
|
||||
layout,
|
||||
over_layout,
|
||||
cursor_position,
|
||||
messages,
|
||||
renderer,
|
||||
@ -147,16 +150,11 @@ where
|
||||
// If mouse press check if over the overlay widget before sending to under
|
||||
// widget
|
||||
if !matches!(&event, Event::Mouse(mouse::Event::ButtonPressed(_)))
|
||||
|| !layout
|
||||
.children()
|
||||
.next()
|
||||
.unwrap()
|
||||
.bounds()
|
||||
.contains(cursor_position)
|
||||
|| !over_layout.bounds().contains(cursor_position)
|
||||
{
|
||||
self.under.on_event(
|
||||
event,
|
||||
layout,
|
||||
children.next().unwrap(),
|
||||
cursor_position,
|
||||
messages,
|
||||
renderer,
|
||||
|
Loading…
Reference in New Issue
Block a user