mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Start rewrite of character selection with iced
This commit is contained in:
parent
34f92c7076
commit
545f3bd44b
assets/voxygen/element/buttons
voxygen/src
menu
ui/ice
BIN
assets/voxygen/element/buttons/x_red.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/buttons/x_red.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/buttons/x_red.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/buttons/x_red.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/buttons/x_red_hover.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/buttons/x_red_hover.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/buttons/x_red_hover.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/buttons/x_red_hover.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/element/buttons/x_red_press.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/buttons/x_red_press.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/buttons/x_red_press.vox
(Stored with Git LFS)
BIN
assets/voxygen/element/buttons/x_red_press.vox
(Stored with Git LFS)
Binary file not shown.
@ -39,16 +39,10 @@ impl CharSelectionState {
|
||||
|
||||
fn get_humanoid_body(&self) -> Option<comp::humanoid::Body> {
|
||||
self.char_selection_ui
|
||||
.get_character_list()
|
||||
.and_then(|data| {
|
||||
if let Some(character) = data.get(self.char_selection_ui.selected_character) {
|
||||
match character.body {
|
||||
comp::Body::Humanoid(body) => Some(body),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.selected_character()
|
||||
.and_then(|character| match character.body {
|
||||
comp::Body::Humanoid(body) => Some(body),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -98,18 +92,10 @@ impl PlayState for CharSelectionState {
|
||||
ui::Event::DeleteCharacter(character_id) => {
|
||||
self.client.borrow_mut().delete_character(character_id);
|
||||
},
|
||||
ui::Event::Play => {
|
||||
let char_data = self
|
||||
.char_selection_ui
|
||||
.get_character_list()
|
||||
.expect("Character data is required to play");
|
||||
|
||||
if let Some(selected_character) =
|
||||
char_data.get(self.char_selection_ui.selected_character)
|
||||
{
|
||||
if let Some(character_id) = selected_character.character.id {
|
||||
self.client.borrow_mut().request_character(character_id);
|
||||
}
|
||||
ui::Event::Play(character) => {
|
||||
// TODO: eliminate option in character id
|
||||
if let Some(character_id) = character.character.id {
|
||||
self.client.borrow_mut().request_character(character_id);
|
||||
}
|
||||
|
||||
return PlayStateResult::Switch(Box::new(SessionState::new(
|
||||
@ -212,6 +198,6 @@ impl PlayState for CharSelectionState {
|
||||
|
||||
// Draw the UI to the screen.
|
||||
self.char_selection_ui
|
||||
.render(renderer, self.scene.globals());
|
||||
.render(global_state.window.renderer_mut());
|
||||
}
|
||||
}
|
||||
|
513
voxygen/src/menu/char_selection/ui/mod.rs
Normal file
513
voxygen/src/menu/char_selection/ui/mod.rs
Normal file
@ -0,0 +1,513 @@
|
||||
use crate::{
|
||||
i18n::{i18n_asset_key, Localization},
|
||||
render::Renderer,
|
||||
ui::{
|
||||
self,
|
||||
fonts::IcedFonts as Fonts,
|
||||
ice::{component::neat_button, style, widget::Overlay, Element, IcedUi as Ui},
|
||||
img_ids::{ImageGraphic, VoxelGraphic},
|
||||
},
|
||||
window, GlobalState,
|
||||
};
|
||||
use client::Client;
|
||||
use common::{
|
||||
character::{CharacterItem, MAX_CHARACTERS_PER_PLAYER},
|
||||
comp,
|
||||
comp::humanoid,
|
||||
};
|
||||
//ImageFrame, Tooltip,
|
||||
use crate::settings::Settings;
|
||||
use common::assets::load_expect;
|
||||
//use std::time::Duration;
|
||||
//use ui::ice::widget;
|
||||
use iced::{
|
||||
button, text_input, Align, Button, Column, Container, HorizontalAlignment, Length, Row, Space,
|
||||
Text,
|
||||
};
|
||||
|
||||
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
|
||||
pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
|
||||
const FILL_FRAC_ONE: f32 = 0.77;
|
||||
const FILL_FRAC_TWO: f32 = 0.53;
|
||||
|
||||
image_ids_ice! {
|
||||
struct Imgs {
|
||||
<VoxelGraphic>
|
||||
// TODO: convert large frames into borders
|
||||
charlist_frame: "voxygen.element.frames.window_4",
|
||||
server_frame: "voxygen.element.frames.server_frame",
|
||||
slider_range: "voxygen.element.slider.track",
|
||||
slider_indicator: "voxygen.element.slider.indicator",
|
||||
|
||||
<ImageGraphic>
|
||||
selection: "voxygen.element.frames.selection",
|
||||
selection_hover: "voxygen.element.frames.selection_hover",
|
||||
selection_press: "voxygen.element.frames.selection_press",
|
||||
|
||||
delete_button: "voxygen.element.buttons.x_red",
|
||||
delete_button_hover: "voxygen.element.buttons.x_red_hover",
|
||||
delete_button_press: "voxygen.element.buttons.x_red_press",
|
||||
|
||||
|
||||
name_input: "voxygen.element.misc_bg.textbox_mid",
|
||||
|
||||
// Tool Icons
|
||||
daggers: "voxygen.element.icons.daggers",
|
||||
sword: "voxygen.element.icons.sword",
|
||||
axe: "voxygen.element.icons.axe",
|
||||
hammer: "voxygen.element.icons.hammer",
|
||||
bow: "voxygen.element.icons.bow",
|
||||
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",
|
||||
orc_f: "voxygen.element.icons.orc_f",
|
||||
dwarf_m: "voxygen.element.icons.dwarf_m",
|
||||
dwarf_f: "voxygen.element.icons.dwarf_f",
|
||||
undead_m: "voxygen.element.icons.ud_m",
|
||||
undead_f: "voxygen.element.icons.ud_f",
|
||||
elf_m: "voxygen.element.icons.elf_m",
|
||||
elf_f: "voxygen.element.icons.elf_f",
|
||||
danari_m: "voxygen.element.icons.danari_m",
|
||||
danari_f: "voxygen.element.icons.danari_f",
|
||||
// Icon Borders
|
||||
icon_border: "voxygen.element.buttons.border",
|
||||
icon_border_mo: "voxygen.element.buttons.border_mo",
|
||||
icon_border_press: "voxygen.element.buttons.border_press",
|
||||
icon_border_pressed: "voxygen.element.buttons.border_pressed",
|
||||
|
||||
<ImageGraphic>
|
||||
button: "voxygen.element.buttons.button",
|
||||
button_hover: "voxygen.element.buttons.button_hover",
|
||||
button_press: "voxygen.element.buttons.button_press",
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: do rotation in widget renderer
|
||||
/*rotation_image_ids! {
|
||||
pub struct ImgsRot {
|
||||
<VoxelGraphic>
|
||||
|
||||
// Tooltip Test
|
||||
tt_side: "voxygen/element/frames/tt_test_edge",
|
||||
tt_corner: "voxygen/element/frames/tt_test_corner_tr",
|
||||
}
|
||||
}*/
|
||||
|
||||
pub enum Event {
|
||||
Logout,
|
||||
Play(CharacterItem),
|
||||
AddCharacter {
|
||||
alias: String,
|
||||
tool: Option<String>,
|
||||
body: comp::Body,
|
||||
},
|
||||
DeleteCharacter(i32),
|
||||
}
|
||||
|
||||
struct CharacterList {
|
||||
characters: Vec<CharacterItem>,
|
||||
selected_character: usize,
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
Select {
|
||||
list: Option<CharacterList>,
|
||||
character_buttons: Vec<button::State>,
|
||||
new_character_button: button::State,
|
||||
|
||||
logout_button: button::State,
|
||||
enter_world_button: button::State,
|
||||
change_server_button: button::State,
|
||||
},
|
||||
Create {
|
||||
name: String, // TODO: default to username
|
||||
body: humanoid::Body,
|
||||
loadout: comp::Loadout,
|
||||
tool: Option<&'static str>,
|
||||
|
||||
name_input: text_input::State,
|
||||
back_button: button::State,
|
||||
create_button: button::State,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum InfoContent {
|
||||
Deletion(usize),
|
||||
LoadingCharacters,
|
||||
CreatingCharacter,
|
||||
DeletingCharacter,
|
||||
CharacterError,
|
||||
}
|
||||
|
||||
/*
|
||||
impl InfoContent {
|
||||
pub fn has_content(&self, character_list_loading: &bool) -> bool {
|
||||
match self {
|
||||
Self::None => false,
|
||||
Self::CreatingCharacter | Self::DeletingCharacter | Self::LoadingCharacters => {
|
||||
*character_list_loading
|
||||
},
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
struct Controls {
|
||||
fonts: Fonts,
|
||||
imgs: Imgs,
|
||||
i18n: std::sync::Arc<Localization>,
|
||||
// Voxygen version
|
||||
version: String,
|
||||
|
||||
info_content: Option<InfoContent>,
|
||||
// enter: bool,
|
||||
mode: Mode,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum Message {
|
||||
Back,
|
||||
Logout,
|
||||
EnterWorld,
|
||||
Delete(usize),
|
||||
ChangeServer,
|
||||
NewCharacter,
|
||||
CreateCharacter,
|
||||
Name(String),
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
fn new(fonts: Fonts, imgs: Imgs, i18n: std::sync::Arc<Localization>) -> Self {
|
||||
let version = format!(
|
||||
"{}-{}",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
common::util::GIT_VERSION.to_string()
|
||||
);
|
||||
|
||||
Self {
|
||||
fonts,
|
||||
imgs,
|
||||
i18n,
|
||||
version,
|
||||
|
||||
info_content: None,
|
||||
mode: Mode::Select {
|
||||
list: None,
|
||||
character_buttons: Vec::new(),
|
||||
new_character_button: Default::default(),
|
||||
logout_button: Default::default(),
|
||||
enter_world_button: Default::default(),
|
||||
change_server_button: Default::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&mut self, settings: &Settings) -> Element<Message> {
|
||||
// TODO: if enter key pressed and character is selected then enter the world
|
||||
// TODO: tooltip widget
|
||||
|
||||
let imgs = &self.imgs;
|
||||
let i18n = &self.i18n;
|
||||
|
||||
let button_style = style::button::Style::new(imgs.button)
|
||||
.hover_image(imgs.button_hover)
|
||||
.press_image(imgs.button_press)
|
||||
.text_color(TEXT_COLOR)
|
||||
.disabled_text_color(DISABLED_TEXT_COLOR);
|
||||
|
||||
let version = iced::Text::new(&self.version)
|
||||
.size(self.fonts.cyri.scale(15))
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(HorizontalAlignment::Right);
|
||||
|
||||
let content = match &mut self.mode {
|
||||
Mode::Select {
|
||||
list,
|
||||
ref mut character_buttons,
|
||||
ref mut new_character_button,
|
||||
ref mut logout_button,
|
||||
ref mut enter_world_button,
|
||||
ref mut change_server_button,
|
||||
} => {
|
||||
// TODO: impl delete prompt as overlay
|
||||
let change_server = Space::new(Length::Units(100), Length::Units(40));
|
||||
let characters = if let Some(list) = list {
|
||||
let num = list.characters.len();
|
||||
// Ensure we have enough button states
|
||||
character_buttons.resize_with(num * 2, Default::default);
|
||||
|
||||
let mut characters = list
|
||||
.characters
|
||||
.iter()
|
||||
.zip(character_buttons.chunks_exact_mut(2))
|
||||
.map(|(character, buttons)| {
|
||||
let mut buttons = buttons.iter_mut();
|
||||
(
|
||||
character,
|
||||
(buttons.next().unwrap(), buttons.next().unwrap()),
|
||||
)
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(i, (character, (select_button, delete_button)))| {
|
||||
Overlay::new(
|
||||
Button::new(select_button, Space::new(Length::Fill, Length::Fill))
|
||||
.width(Length::Units(20))
|
||||
.height(Length::Units(20))
|
||||
.style(
|
||||
style::button::Style::new(imgs.delete_button)
|
||||
.hover_image(imgs.delete_button_hover)
|
||||
.press_image(imgs.delete_button_press),
|
||||
),
|
||||
Button::new(
|
||||
delete_button,
|
||||
Column::with_children(vec![
|
||||
Text::new("Hi").into(),
|
||||
Text::new("Hi").into(),
|
||||
Text::new("Hi").into(),
|
||||
]),
|
||||
)
|
||||
.style(
|
||||
style::button::Style::new(imgs.selection)
|
||||
.hover_image(imgs.selection_hover)
|
||||
.press_image(imgs.selection_press),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Add create new character button
|
||||
let color = if num >= MAX_CHARACTERS_PER_PLAYER {
|
||||
iced::Color::from_rgb8(97, 97, 25)
|
||||
} else {
|
||||
iced::Color::from_rgb8(97, 255, 18)
|
||||
};
|
||||
characters.push(
|
||||
Button::new(
|
||||
new_character_button,
|
||||
Text::new(i18n.get("char_selection.create_new_character")),
|
||||
)
|
||||
.style(
|
||||
style::button::Style::new(imgs.selection)
|
||||
.hover_image(imgs.selection_hover)
|
||||
.press_image(imgs.selection_press)
|
||||
.image_color(color)
|
||||
.text_color(color),
|
||||
)
|
||||
.into(),
|
||||
);
|
||||
characters
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let characters = Column::with_children(characters);
|
||||
|
||||
let right_column =
|
||||
Column::with_children(vec![change_server.into(), characters.into()])
|
||||
.spacing(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.max_width(300);
|
||||
|
||||
let top = Container::new(right_column)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill);
|
||||
|
||||
let logout = neat_button(
|
||||
logout_button,
|
||||
i18n.get("char_selection.logout"),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
Some(Message::Logout),
|
||||
);
|
||||
|
||||
let enter_world = neat_button(
|
||||
enter_world_button,
|
||||
i18n.get("char_selection.enter_world"),
|
||||
FILL_FRAC_ONE,
|
||||
button_style,
|
||||
Some(Message::EnterWorld),
|
||||
);
|
||||
|
||||
let bottom = Row::with_children(vec![
|
||||
Container::new(logout)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Units(40))
|
||||
.align_y(Align::End)
|
||||
.into(),
|
||||
Container::new(enter_world)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Units(60))
|
||||
.center_x()
|
||||
.align_y(Align::End)
|
||||
.into(),
|
||||
Space::new(Length::Fill, Length::Shrink).into(),
|
||||
]);
|
||||
|
||||
Column::with_children(vec![top.into(), bottom.into()])
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
},
|
||||
Mode::Create {
|
||||
name,
|
||||
body,
|
||||
loadout,
|
||||
tool,
|
||||
name_input,
|
||||
back_button,
|
||||
create_button,
|
||||
} => {
|
||||
let top_row = Row::with_children(vec![]);
|
||||
let bottom_row = Row::with_children(vec![]);
|
||||
|
||||
Column::with_children(vec![top_row.into(), bottom_row.into()])
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
},
|
||||
};
|
||||
|
||||
Container::new(
|
||||
Column::with_children(vec![version.into(), content.into()])
|
||||
.spacing(3)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
)
|
||||
.padding(3)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message, events: &mut Vec<Event>, settings: &Settings) {
|
||||
let servers = &settings.networking.servers;
|
||||
|
||||
//match message { }
|
||||
}
|
||||
|
||||
pub fn selected_character(&self) -> Option<&CharacterItem> {
|
||||
match self.mode {
|
||||
// TODO
|
||||
Mode::Select { .. } => None,
|
||||
// TODO
|
||||
Mode::Create { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CharSelectionUi {
|
||||
ui: Ui,
|
||||
controls: Controls,
|
||||
}
|
||||
|
||||
impl CharSelectionUi {
|
||||
pub fn new(global_state: &mut GlobalState) -> Self {
|
||||
// Load language
|
||||
let i18n = load_expect::<Localization>(&i18n_asset_key(
|
||||
&global_state.settings.language.selected_language,
|
||||
));
|
||||
|
||||
// TODO: don't add default font twice
|
||||
let font = {
|
||||
use std::io::Read;
|
||||
let mut buf = Vec::new();
|
||||
common::assets::load_file("voxygen.font.haxrcorp_4089_cyrillic_altgr_extended", &[
|
||||
"ttf",
|
||||
])
|
||||
.unwrap()
|
||||
.read_to_end(&mut buf)
|
||||
.unwrap();
|
||||
ui::ice::Font::try_from_vec(buf).unwrap()
|
||||
};
|
||||
|
||||
let mut ui = Ui::new(&mut global_state.window, font).unwrap();
|
||||
|
||||
let fonts = Fonts::load(&i18n.fonts, &mut ui).expect("Impossible to load fonts");
|
||||
|
||||
let controls = Controls::new(
|
||||
fonts,
|
||||
Imgs::load(&mut ui).expect("Failed to load images"),
|
||||
i18n,
|
||||
);
|
||||
|
||||
Self { ui, controls }
|
||||
}
|
||||
|
||||
pub fn selected_character(&self) -> Option<&CharacterItem> {
|
||||
self.controls.selected_character()
|
||||
}
|
||||
|
||||
// TODO
|
||||
pub fn get_loadout(&mut self) -> Option<comp::Loadout> {
|
||||
// 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: (*load_expect::<comp::Item>(tool)).clone(),
|
||||
ability1: None,
|
||||
ability2: None,
|
||||
ability3: None,
|
||||
block_ability: None,
|
||||
dodge_ability: None,
|
||||
});
|
||||
loadout.chest = Some(assets::load_expect_cloned(
|
||||
"common.items.armor.starter.rugged_chest",
|
||||
));
|
||||
loadout.pants = Some(assets::load_expect_cloned(
|
||||
"common.items.armor.starter.rugged_pants",
|
||||
));
|
||||
loadout.foot = Some(assets::load_expect_cloned(
|
||||
"common.items.armor.starter.sandals_0",
|
||||
));
|
||||
Some(loadout.clone())
|
||||
},
|
||||
}*/
|
||||
None
|
||||
}
|
||||
|
||||
pub fn handle_event(&mut self, event: window::Event) -> bool {
|
||||
match event {
|
||||
window::Event::IcedUi(event) => {
|
||||
self.ui.handle_event(event);
|
||||
true
|
||||
},
|
||||
window::Event::MouseButton(_, window::PressState::Pressed) => {
|
||||
// TODO: implement this with iced
|
||||
// !self.ui.no_widget_capturing_mouse()
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maintain(&mut self, global_state: &mut GlobalState, client: &mut Client) -> Vec<Event> {
|
||||
let mut events = Vec::new();
|
||||
|
||||
let (messages, _) = self.ui.maintain(
|
||||
self.controls.view(&global_state.settings),
|
||||
global_state.window.renderer_mut(),
|
||||
);
|
||||
|
||||
messages.into_iter().for_each(|message| {
|
||||
self.controls
|
||||
.update(message, &mut events, &global_state.settings)
|
||||
});
|
||||
|
||||
events
|
||||
}
|
||||
|
||||
// TODO: do we need globals
|
||||
pub fn render(&self, renderer: &mut Renderer) { self.ui.render(renderer); }
|
||||
}
|
@ -17,8 +17,6 @@ use crate::{
|
||||
use iced::{button, text_input, Align, Column, Container, Length, Row, Space, Text, TextInput};
|
||||
use vek::*;
|
||||
|
||||
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
|
||||
pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
|
||||
const FILL_FRAC_ONE: f32 = 0.77;
|
||||
const FILL_FRAC_TWO: f32 = 0.53;
|
||||
const INPUT_WIDTH: u16 = 250;
|
||||
|
@ -9,19 +9,19 @@ use crate::{
|
||||
ui::{
|
||||
self,
|
||||
fonts::IcedFonts as Fonts,
|
||||
ice::{Element, IcedUi as Ui},
|
||||
ice::{style, widget, Element, Font, IcedUi as Ui},
|
||||
img_ids::{ImageGraphic, VoxelGraphic},
|
||||
Graphic,
|
||||
},
|
||||
GlobalState,
|
||||
};
|
||||
use iced::{text_input, Column, Container, HorizontalAlignment, Length};
|
||||
//ImageFrame, Tooltip,
|
||||
use crate::settings::Settings;
|
||||
use common::assets::Asset;
|
||||
use image::DynamicImage;
|
||||
use rand::{seq::SliceRandom, thread_rng, Rng};
|
||||
use std::time::Duration;
|
||||
use ui::ice::widget;
|
||||
|
||||
// TODO: what is this? (showed up in rebase)
|
||||
//const COL1: Color = Color::Rgba(0.07, 0.1, 0.1, 0.9);
|
||||
@ -30,8 +30,9 @@ use ui::ice::widget;
|
||||
/*const UI_MAIN: Color = Color::Rgba(0.61, 0.70, 0.70, 1.0); // Greenish Blue
|
||||
const UI_HIGHLIGHT_0: Color = Color::Rgba(0.79, 1.09, 1.09, 1.0);*/
|
||||
|
||||
use iced::{text_input, Column, Container, HorizontalAlignment, Length};
|
||||
use ui::ice::style;
|
||||
pub const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0);
|
||||
pub const DISABLED_TEXT_COLOR: iced::Color = iced::Color::from_rgba(1.0, 1.0, 1.0, 0.2);
|
||||
|
||||
image_ids_ice! {
|
||||
struct Imgs {
|
||||
<VoxelGraphic>
|
||||
@ -227,8 +228,8 @@ impl Controls {
|
||||
let button_style = style::button::Style::new(self.imgs.button)
|
||||
.hover_image(self.imgs.button_hover)
|
||||
.press_image(self.imgs.button_press)
|
||||
.text_color(login::TEXT_COLOR)
|
||||
.disabled_text_color(login::DISABLED_TEXT_COLOR);
|
||||
.text_color(TEXT_COLOR)
|
||||
.disabled_text_color(DISABLED_TEXT_COLOR);
|
||||
|
||||
let version = iced::Text::new(&self.version)
|
||||
.size(self.fonts.cyri.scale(15))
|
||||
@ -432,7 +433,6 @@ pub struct MainMenuUi {
|
||||
ui: Ui,
|
||||
// TODO: re add this
|
||||
// tip_no: u16,
|
||||
pub show_iced: bool,
|
||||
controls: Controls,
|
||||
}
|
||||
|
||||
@ -453,7 +453,7 @@ impl<'a> MainMenuUi {
|
||||
.unwrap()
|
||||
.read_to_end(&mut buf)
|
||||
.unwrap();
|
||||
ui::ice::Font::try_from_vec(buf).unwrap()
|
||||
Font::try_from_vec(buf).unwrap()
|
||||
};
|
||||
|
||||
let mut ui = Ui::new(&mut global_state.window, font).unwrap();
|
||||
|
@ -6,6 +6,7 @@ struct Background {
|
||||
default: image::Handle,
|
||||
hover: image::Handle,
|
||||
press: image::Handle,
|
||||
color: Color,
|
||||
}
|
||||
|
||||
impl Background {
|
||||
@ -14,6 +15,7 @@ impl Background {
|
||||
default: image,
|
||||
hover: image,
|
||||
press: image,
|
||||
color: Color::WHITE,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -58,6 +60,15 @@ impl Style {
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: this needs to be refactored since the color isn't used if there is no
|
||||
// background
|
||||
pub fn image_color(mut self, color: Color) -> Self {
|
||||
if let Some(background) = &mut self.background {
|
||||
background.color = color;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn text_color(mut self, color: Color) -> Self {
|
||||
self.enabled_text = color;
|
||||
self
|
||||
|
@ -5,6 +5,7 @@ mod column;
|
||||
mod compound_graphic;
|
||||
mod container;
|
||||
mod image;
|
||||
mod overlay;
|
||||
mod row;
|
||||
mod scrollable;
|
||||
mod slider;
|
||||
|
36
voxygen/src/ui/ice/renderer/widget/overlay.rs
Normal file
36
voxygen/src/ui/ice/renderer/widget/overlay.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use super::super::{super::widget::overlay, IcedRenderer, Primitive};
|
||||
use iced::{mouse::Interaction, Element, Layout, Point, Rectangle};
|
||||
|
||||
const BORDER_SIZE: u16 = 8;
|
||||
|
||||
impl overlay::Renderer for IcedRenderer {
|
||||
fn draw<M>(
|
||||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
_bounds: Rectangle,
|
||||
cursor_position: Point,
|
||||
over: &Element<'_, M, Self>,
|
||||
over_layout: Layout<'_>,
|
||||
under: &Element<'_, M, Self>,
|
||||
under_layout: Layout<'_>,
|
||||
) -> Self::Output {
|
||||
let (under, under_mouse_interaction) =
|
||||
under.draw(self, defaults, under_layout, cursor_position);
|
||||
|
||||
let (over, over_mouse_interaction) =
|
||||
over.draw(self, defaults, over_layout, cursor_position);
|
||||
|
||||
// TODO: this isn't perfect but should be obselete when iced gets layer support
|
||||
let mouse_interaction = if over_mouse_interaction == Interaction::Idle {
|
||||
under_mouse_interaction
|
||||
} else {
|
||||
over_mouse_interaction
|
||||
};
|
||||
|
||||
let prim = Primitive::Group {
|
||||
primitives: vec![under, over],
|
||||
};
|
||||
|
||||
(prim, mouse_interaction)
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ pub mod background_container;
|
||||
pub mod compound_graphic;
|
||||
pub mod fill_text;
|
||||
pub mod image;
|
||||
pub mod overlay;
|
||||
pub mod stack;
|
||||
|
||||
pub use self::{
|
||||
@ -10,4 +11,5 @@ pub use self::{
|
||||
background_container::{BackgroundContainer, Padding},
|
||||
fill_text::FillText,
|
||||
image::Image,
|
||||
overlay::Overlay,
|
||||
};
|
||||
|
222
voxygen/src/ui/ice/widget/overlay.rs
Normal file
222
voxygen/src/ui/ice/widget/overlay.rs
Normal file
@ -0,0 +1,222 @@
|
||||
use iced::{
|
||||
layout, mouse, Align, Clipboard, Element, Event, Hasher, Layout, Length, Point, Rectangle,
|
||||
Size, Widget,
|
||||
};
|
||||
use std::hash::Hash;
|
||||
|
||||
/// A widget used to overlay one widget on top of another
|
||||
/// Layout behaves similar to the iced::Container widget
|
||||
/// Manages filtering out mouse input for the back widget if the mouse is over
|
||||
/// the front widget
|
||||
/// Alignment and padding is used for the front widget
|
||||
pub struct Overlay<'a, M, R: self::Renderer> {
|
||||
padding: u16,
|
||||
width: Length,
|
||||
height: Length,
|
||||
max_width: u32,
|
||||
max_height: u32,
|
||||
horizontal_alignment: Align,
|
||||
vertical_alignment: Align,
|
||||
over: Element<'a, M, R>,
|
||||
under: Element<'a, M, R>,
|
||||
// add style etc as needed
|
||||
}
|
||||
|
||||
impl<'a, M, R> Overlay<'a, M, R>
|
||||
where
|
||||
R: self::Renderer,
|
||||
{
|
||||
pub fn new<O, U>(over: O, under: U) -> Self
|
||||
where
|
||||
O: Into<Element<'a, M, R>>,
|
||||
U: Into<Element<'a, M, R>>,
|
||||
{
|
||||
Self {
|
||||
padding: 0,
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
max_width: u32::MAX,
|
||||
max_height: u32::MAX,
|
||||
horizontal_alignment: Align::Start,
|
||||
vertical_alignment: Align::Start,
|
||||
over: over.into(),
|
||||
under: under.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn padding(mut self, pad: u16) -> Self {
|
||||
self.padding = pad;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn width(mut self, width: Length) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn height(mut self, height: Length) -> Self {
|
||||
self.height = height;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn max_width(mut self, max_width: u32) -> Self {
|
||||
self.max_width = max_width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn max_height(mut self, max_height: u32) -> Self {
|
||||
self.max_height = max_height;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn align_x(mut self, align_x: Align) -> Self {
|
||||
self.horizontal_alignment = align_x;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn align_y(mut self, align_y: Align) -> Self {
|
||||
self.vertical_alignment = align_y;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn center_x(mut self) -> Self {
|
||||
self.horizontal_alignment = Align::Center;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn center_y(mut self) -> Self {
|
||||
self.vertical_alignment = Align::Center;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, M, R> Widget<M, R> for Overlay<'a, M, R>
|
||||
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 padding = self.padding as f32;
|
||||
|
||||
let limits = limits
|
||||
.loose()
|
||||
.max_width(self.max_width)
|
||||
.max_height(self.max_height)
|
||||
.width(self.width)
|
||||
.height(self.height);
|
||||
|
||||
let under = self.under.layout(renderer, &limits.loose());
|
||||
let under_size = under.size();
|
||||
|
||||
let limits = limits.pad(padding);
|
||||
let mut over = self.under.layout(renderer, &limits.loose());
|
||||
let over_size = over.size();
|
||||
|
||||
let size = limits.resolve(Size {
|
||||
width: under_size.width.max(over_size.width),
|
||||
height: under_size.width.max(over_size.width),
|
||||
});
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
messages: &mut Vec<M>,
|
||||
renderer: &R,
|
||||
clipboard: Option<&dyn Clipboard>,
|
||||
) {
|
||||
self.over.on_event(
|
||||
event.clone(),
|
||||
layout,
|
||||
cursor_position,
|
||||
messages,
|
||||
renderer,
|
||||
clipboard,
|
||||
);
|
||||
|
||||
// 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)
|
||||
{
|
||||
self.under.on_event(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
messages,
|
||||
renderer,
|
||||
clipboard,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut R,
|
||||
defaults: &R::Defaults,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) -> R::Output {
|
||||
let mut children = layout.children();
|
||||
renderer.draw(
|
||||
defaults,
|
||||
layout.bounds(),
|
||||
cursor_position,
|
||||
&self.over,
|
||||
children.next().unwrap(),
|
||||
&self.under,
|
||||
children.next().unwrap(),
|
||||
)
|
||||
}
|
||||
|
||||
fn hash_layout(&self, state: &mut Hasher) {
|
||||
struct Marker;
|
||||
std::any::TypeId::of::<Marker>().hash(state);
|
||||
|
||||
self.padding.hash(state);
|
||||
self.width.hash(state);
|
||||
self.height.hash(state);
|
||||
self.max_width.hash(state);
|
||||
self.max_height.hash(state);
|
||||
|
||||
self.over.hash_layout(state);
|
||||
self.under.hash_layout(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Renderer: iced::Renderer {
|
||||
fn draw<M>(
|
||||
&mut self,
|
||||
defaults: &Self::Defaults,
|
||||
bounds: Rectangle,
|
||||
cursor_position: Point,
|
||||
//style: &self::Style,
|
||||
over: &Element<'_, M, Self>,
|
||||
over_layout: Layout<'_>,
|
||||
under: &Element<'_, M, Self>,
|
||||
under_layout: Layout<'_>,
|
||||
) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<'a, M, R> From<Overlay<'a, M, R>> for Element<'a, M, R>
|
||||
where
|
||||
R: 'a + self::Renderer,
|
||||
M: 'a,
|
||||
{
|
||||
fn from(overlay: Overlay<'a, M, R>) -> Element<'a, M, R> { Element::new(overlay) }
|
||||
}
|
Loading…
Reference in New Issue
Block a user