diff --git a/Cargo.lock b/Cargo.lock index d6cde5f45c..929b764240 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2066,12 +2066,12 @@ dependencies = [ [[package]] name = "iced_core" version = "0.2.1" -source = "git+https://github.com/hecrj/iced#f46431600cb61d4e83e0ded1ca79525478436be3" +source = "git+https://github.com/Imberflur/iced?branch=text-clone#0a775191abad5787af3aaa302d5599ef12060264" [[package]] name = "iced_futures" version = "0.1.2" -source = "git+https://github.com/hecrj/iced#f46431600cb61d4e83e0ded1ca79525478436be3" +source = "git+https://github.com/Imberflur/iced?branch=text-clone#0a775191abad5787af3aaa302d5599ef12060264" dependencies = [ "futures 0.3.5", "log", @@ -2081,11 +2081,11 @@ dependencies = [ [[package]] name = "iced_native" version = "0.2.2" -source = "git+https://github.com/hecrj/iced#f46431600cb61d4e83e0ded1ca79525478436be3" +source = "git+https://github.com/Imberflur/iced?branch=text-clone#0a775191abad5787af3aaa302d5599ef12060264" dependencies = [ "iced_core", "iced_futures", - "num-traits 0.2.12", + "raw-window-handle", "twox-hash", "unicode-segmentation", ] diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index c4b3208c1c..435d395271 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -35,7 +35,7 @@ winit = {version = "0.22.2", features = ["serde"]} conrod_core = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"} conrod_winit = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"} euc = {git = "https://github.com/zesterer/euc.git"} -iced = {package = "iced_native", git = "https://github.com/hecrj/iced"} +iced = {package = "iced_native", git = "https://github.com/Imberflur/iced", branch = "text-clone"} window_clipboard = "0.1.1" glyph_brush = "0.6.3" diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index 69bcaf6b77..c3d6b25163 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -184,38 +184,55 @@ pub struct PopupData { popup_type: PopupType, } -// No state currently +use ui::ice::component::neat_button; struct IcedState { imgs: IcedImgs, - quit_button: iced::button::State, + quit_button: neat_button::State, + settings_button: neat_button::State, + servers_button: neat_button::State, + multiplayer_button: neat_button::State, + #[cfg(feature = "singleplayer")] + singleplayer_button: neat_button::State, + show_servers: bool, } #[derive(Clone)] // TODO: why does iced require Clone? enum Message { Quit, + ShowServers, + #[cfg(feature = "singleplayer")] + Singleplayer, + Multiplayer, } impl IcedState { pub fn new(imgs: IcedImgs) -> Self { Self { imgs, - quit_button: iced::button::State::new(), + servers_button: Default::default(), + settings_button: Default::default(), + quit_button: Default::default(), + multiplayer_button: Default::default(), + #[cfg(feature = "singleplayer")] + singleplayer_button: Default::default(), + show_servers: false, } } pub fn view(&mut self, i18n: &Localization) -> Element { - // TODO: scale with window size - let button_font_size = 30; + //let button_font_size = 30; const TEXT_COLOR: iced::Color = iced::Color::from_rgb(1.0, 1.0, 1.0); 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; - use iced::{ - Align, Button, Column, Container, HorizontalAlignment, Length, Row, Space, Text, - VerticalAlignment, - }; + use iced::{Align, Column, Container, Length, Row, Space}; use ui::ice::{ - compound_graphic::{CompoundGraphic, Graphic}, - AspectRatioContainer, BackgroundContainer, ButtonStyle, Image, Padding, + widget::{ + compound_graphic::{CompoundGraphic, Graphic}, + BackgroundContainer, Image, Padding, + }, + ButtonStyle, }; use vek::*; @@ -224,50 +241,31 @@ impl IcedState { .press_image(self.imgs.button_press) .text_color(TEXT_COLOR) .disabled_text_color(DISABLED_TEXT_COLOR); - let buttons = Column::with_children(vec![ - Image::new(self.imgs.button).fix_aspect_ratio().into(), - Image::new(self.imgs.button).fix_aspect_ratio().into(), - AspectRatioContainer::new( - Button::new( - &mut self.quit_button, - Text::new(i18n.get("common.quit")) - .size(button_font_size) - .height(Length::Fill) - .width(Length::Fill) - .horizontal_alignment(HorizontalAlignment::Center) - .vertical_alignment(VerticalAlignment::Center), - ) - .height(Length::Fill) - .width(Length::Fill) - .style(button_style) - .on_press(Message::Quit), - ) - .ratio_of_image(self.imgs.button) - .into(), + self.servers_button.view( + i18n.get("common.servers"), + FILL_FRAC_ONE, + button_style, + Some(Message::ShowServers), + ), + self.settings_button.view( + i18n.get("common.settings"), + FILL_FRAC_ONE, + button_style, + None, + ), + self.quit_button.view( + i18n.get("common.quit"), + FILL_FRAC_ONE, + button_style, + Some(Message::Quit), + ), ]) .width(Length::Fill) .max_width(200) .spacing(5) .padding(10); - // Quit - /*if Button::image(self.imgs.button) - .w_h(190.0, 40.0) - .bottom_left_with_margins_on(ui_widgets.window, 60.0, 30.0) - .hover_image(self.imgs.button_hover) - .press_image(self.imgs.button_press) - .label(i18n.get("common.quit")) - .label_font_id(self.fonts.cyri.conrod_id) - .label_color(TEXT_COLOR) - .label_font_size(self.fonts.cyri.scale(20)) - .label_y(Relative::Scalar(3.0)) - .set(self.ids.quit_button, ui_widgets) - .was_clicked() - { - events.push(Event::Quit); - }*/ - let buttons = Container::new(buttons) .width(Length::Fill) .height(Length::Fill) @@ -303,20 +301,22 @@ impl IcedState { .height(Length::FillPortion(50)) .into(), Column::with_children(vec![ - BackgroundContainer::new( - CompoundGraphic::padded_image(self.imgs.button, [106, 26], [10, 0, 10, 2]) - .fix_aspect_ratio(), - Space::new(Length::Fill, Length::Fill), - ) - .into(), - BackgroundContainer::new( - CompoundGraphic::padded_image(self.imgs.button, [106, 26], [10, 2, 10, 0]) - .fix_aspect_ratio(), - Space::new(Length::Fill, Length::Fill), - ) - .into(), + self.multiplayer_button.view( + i18n.get("common.multiplayer"), + FILL_FRAC_TWO, + button_style, + Some(Message::Multiplayer), + ), + #[cfg(feature = "singleplayer")] + self.singleplayer_button.view( + i18n.get("common.singleplayer"), + FILL_FRAC_TWO, + button_style, + Some(Message::Singleplayer), + ), ]) .max_width(240) + .spacing(8) // TODO scale with available window size, awkward because both width and height can become limiting factors, might need custom column, could also just use fill portion .height(Length::FillPortion(25)) .into(), ]) @@ -358,6 +358,10 @@ impl IcedState { pub fn update(&mut self, message: Message, events: &mut Vec) { match message { Message::Quit => events.push(Event::Quit), + Message::ShowServers => self.show_servers = true, + #[cfg(feature = "singleplayer")] + Message::Singleplayer => events.push(Event::StartSingleplayer), + Message::Multiplayer => (), //TODO } } } diff --git a/voxygen/src/ui/ice/component/mod.rs b/voxygen/src/ui/ice/component/mod.rs new file mode 100644 index 0000000000..7f44faef0b --- /dev/null +++ b/voxygen/src/ui/ice/component/mod.rs @@ -0,0 +1 @@ +pub mod neat_button; diff --git a/voxygen/src/ui/ice/component/neat_button.rs b/voxygen/src/ui/ice/component/neat_button.rs new file mode 100644 index 0000000000..f0ce7d576c --- /dev/null +++ b/voxygen/src/ui/ice/component/neat_button.rs @@ -0,0 +1,44 @@ +use crate::ui::ice as ui; +use iced::{Button, Element, Length}; +use ui::{ + widget::{AspectRatioContainer, FillText}, + ButtonStyle, +}; + +#[derive(Default)] +pub struct State { + state: iced::button::State, +} + +impl State { + pub fn new() -> Self { Self::default() } + + pub fn view( + &mut self, + label: impl Into, + fill_fraction: f32, + button_style: ButtonStyle, + message: Option, + ) -> Element { + let button = Button::new( + &mut self.state, + FillText::new(label).fill_fraction(fill_fraction), + ) + .height(Length::Fill) + .width(Length::Fill) + .style(button_style); + + let button = match message { + Some(message) => button.on_press(message), + None => button, + }; + + let container = AspectRatioContainer::new(button); + let container = match button_style.active().0 { + Some(img) => container.ratio_of_image(img), + None => container, + }; + + container.into() + } +} diff --git a/voxygen/src/ui/ice/mod.rs b/voxygen/src/ui/ice/mod.rs index a3e8745b98..b7791e4638 100644 --- a/voxygen/src/ui/ice/mod.rs +++ b/voxygen/src/ui/ice/mod.rs @@ -1,20 +1,15 @@ // tooltip_manager: TooltipManager, mod cache; mod clipboard; +pub mod component; mod renderer; -mod widget; +pub mod widget; mod winit_conversion; pub use cache::Font; pub use graphic::{Id, Rotation}; pub use iced::Event; pub use renderer::{ButtonStyle, IcedRenderer}; -pub use widget::{ - aspect_ratio_container::AspectRatioContainer, - background_container::{BackgroundContainer, Padding}, - compound_graphic, - image::Image, -}; pub use winit_conversion::window_event; use super::{ diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index f94e576660..193e778842 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -227,7 +227,7 @@ impl IcedRenderer { .collect::>(); if let Err(err) = renderer.update_texture(cache_tex, offset, size, &new_data) { - log::warn!("Failed to update glyph cache texture: {:?}", err); + tracing::warn!("Failed to update glyph cache texture: {:?}", err); } }, // Urgh more allocation we don't need @@ -278,7 +278,7 @@ impl IcedRenderer { }); }, Err(glyph_brush::BrushError::TextureTooSmall { suggested: (x, y) }) => { - log::error!( + tracing::error!( "Texture to small for all glyphs, would need one of the size: ({}, {})", x, y diff --git a/voxygen/src/ui/ice/widget.rs b/voxygen/src/ui/ice/widget.rs deleted file mode 100644 index 85b4968588..0000000000 --- a/voxygen/src/ui/ice/widget.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod aspect_ratio_container; -pub mod background_container; -pub mod compound_graphic; -pub mod image; -pub mod stack; diff --git a/voxygen/src/ui/ice/widget/fill_text.rs b/voxygen/src/ui/ice/widget/fill_text.rs new file mode 100644 index 0000000000..7cd5b8d103 --- /dev/null +++ b/voxygen/src/ui/ice/widget/fill_text.rs @@ -0,0 +1,124 @@ +use iced::{layout, Element, Hasher, Layout, Length, Point, Size, Widget}; +use std::hash::Hash; + +const DEFAULT_FILL_FRACTION: f32 = 1.0; +const DEFAULT_VERTICAL_ADJUSTMENT: f32 = 0.05; + +/// Wraps the existing Text widget giving it more advanced layouting +/// capabilities +/// Centers child text widget and adjust the font size depending on the height +/// of the limits Assumes single line text is being used +pub struct FillText +where + R: iced::text::Renderer, +{ + //max_font_size: u16, uncomment if there is a use case for this + /// Portion of the height of the limits which the font size should be + fill_fraction: f32, + /// Adjustment factor to center the text vertically + /// Multiplied by font size and used to move the text up if positive + // TODO: use the produced glyph geometry directly to do this and/or add support to + // layouting library + vertical_adjustment: f32, + text: iced::Text, +} + +impl FillText +where + R: iced::text::Renderer, +{ + pub fn new(label: impl Into) -> Self { + Self { + //max_font_size: u16::MAX, + fill_fraction: DEFAULT_FILL_FRACTION, + vertical_adjustment: DEFAULT_VERTICAL_ADJUSTMENT, + text: iced::Text::new(label), + } + } + + pub fn fill_fraction(mut self, fraction: f32) -> Self { + self.fill_fraction = fraction; + self + } + + pub fn vertical_adjustment(mut self, adjustment: f32) -> Self { + self.vertical_adjustment = adjustment; + self + } + + pub fn color(mut self, color: impl Into) -> Self { + self.text = self.text.color(color); + self + } + + pub fn font(mut self, font: impl Into) -> Self { + self.text = self.text.font(font); + self + } +} + +impl Widget for FillText +where + R: iced::text::Renderer, +{ + fn width(&self) -> Length { Length::Fill } + + fn height(&self) -> Length { Length::Fill } + + fn layout(&self, renderer: &R, limits: &layout::Limits) -> layout::Node { + let limits = limits.width(Length::Fill).height(Length::Fill); + + let size = limits.max(); + + let font_size = (size.height * self.fill_fraction) as u16; + + let mut text = + Widget::::layout(&self.text.clone().size(font_size), renderer, &limits); + + // Size adjusted for centering + text.align( + iced::Align::Center, + iced::Align::Center, + Size::new( + size.width, + size.height - 2.0 * font_size as f32 * self.vertical_adjustment, + ), + ); + + layout::Node::with_children(size, vec![text]) + } + + fn draw( + &self, + renderer: &mut R, + defaults: &R::Defaults, + layout: Layout<'_>, + cursor_position: Point, + ) -> R::Output { + // Note: this breaks if the parent widget adjusts the bounds height + let font_size = (layout.bounds().height * self.fill_fraction) as u16; + Widget::::draw( + &self.text.clone().size(font_size), + renderer, + defaults, + layout.children().next().unwrap(), + cursor_position, + ) + } + + fn hash_layout(&self, state: &mut Hasher) { + struct Marker; + std::any::TypeId::of::().hash(state); + + self.fill_fraction.to_bits().hash(state); + self.vertical_adjustment.to_bits().hash(state); + Widget::::hash_layout(&self.text, state); + } +} + +impl<'a, M, R> From> for Element<'a, M, R> +where + R: 'a + iced::text::Renderer, +{ + fn from(fill_text: FillText) -> Element<'a, M, R> { Element::new(fill_text) } +} diff --git a/voxygen/src/ui/ice/widget/mod.rs b/voxygen/src/ui/ice/widget/mod.rs new file mode 100644 index 0000000000..18034b58ec --- /dev/null +++ b/voxygen/src/ui/ice/widget/mod.rs @@ -0,0 +1,13 @@ +pub mod aspect_ratio_container; +pub mod background_container; +pub mod compound_graphic; +pub mod fill_text; +pub mod image; +pub mod stack; + +pub use self::{ + aspect_ratio_container::AspectRatioContainer, + background_container::{BackgroundContainer, Padding}, + fill_text::FillText, + image::Image, +};