diff --git a/Cargo.lock b/Cargo.lock index 929b764240..1032afa1ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,15 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "ab_glyph" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a685fe66654266f321a8b572660953f4df36a2135706503a4c89981d76e1a2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser 0.8.0", +] + [[package]] name = "ab_glyph_rasterizer" version = "0.1.3" @@ -117,6 +127,15 @@ dependencies = [ "num-traits 0.2.12", ] +[[package]] +name = "approx" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2a05fd1bd10b2527e20a2cd32d8873d115b8b39fe219ee25f42a8aca6ba278" +dependencies = [ + "num-traits 0.2.12", +] + [[package]] name = "arc-swap" version = "0.4.7" @@ -192,7 +211,7 @@ dependencies = [ "async-task", "broadcaster", "crossbeam-channel 0.4.4", - "crossbeam-deque", + "crossbeam-deque 0.7.3", "crossbeam-utils 0.7.2", "futures-core", "futures-io", @@ -717,6 +736,12 @@ dependencies = [ "syn 1.0.42", ] +[[package]] +name = "const_fn" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c478836e029dcef17fb47c89023448c64f781a046e0300e257ad8225ae59afab" + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -943,7 +968,7 @@ checksum = "2d818a4990769aac0c7ff1360e233ef3a41adcb009ebb2036bf6915eb0f6b23c" dependencies = [ "cfg-if 0.1.10", "crossbeam-channel 0.3.9", - "crossbeam-deque", + "crossbeam-deque 0.7.3", "crossbeam-epoch 0.7.2", "crossbeam-queue 0.1.2", "crossbeam-utils 0.6.6", @@ -968,6 +993,16 @@ dependencies = [ "maybe-uninit", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils 0.8.0", +] + [[package]] name = "crossbeam-deque" version = "0.7.3" @@ -979,6 +1014,17 @@ dependencies = [ "maybe-uninit", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch 0.9.0", + "crossbeam-utils 0.8.0", +] + [[package]] name = "crossbeam-epoch" version = "0.7.2" @@ -1008,6 +1054,20 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "crossbeam-epoch" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f" +dependencies = [ + "cfg-if 1.0.0", + "const_fn", + "crossbeam-utils 0.8.0", + "lazy_static", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-queue" version = "0.1.2" @@ -1049,6 +1109,18 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5" +dependencies = [ + "autocfg 1.0.1", + "cfg-if 1.0.0", + "const_fn", + "lazy_static", +] + [[package]] name = "crossterm" version = "0.17.7" @@ -1858,26 +1930,40 @@ dependencies = [ [[package]] name = "glyph_brush" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fca6f9d679bff1322c76c9a1ad4b8553b30a94f3f75bea6936e19032c2f2ec3" +checksum = "afd3e2cfd503a5218dd56172a8bf7c8655a4a7cf745737c606a6edfeea1b343f" dependencies = [ + "glyph_brush_draw_cache", "glyph_brush_layout", "log", "ordered-float 1.1.0", "rustc-hash", - "rusttype 0.8.3", "twox-hash", ] [[package]] -name = "glyph_brush_layout" -version = "0.1.9" +name = "glyph_brush_draw_cache" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b70adc570f1dc71b6b32e241cbcc2b42175f5aea71951fbf41e68b04aec24c7" +checksum = "8cef969a091be5565c2c10b31fd2f115cbeed9f783a27c96ae240ff8ceee067c" dependencies = [ - "approx", - "rusttype 0.8.3", + "ab_glyph", + "crossbeam-channel 0.5.0", + "crossbeam-deque 0.8.0", + "linked-hash-map", + "rayon", + "rustc-hash", +] + +[[package]] +name = "glyph_brush_layout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10bc06d530bf20c1902f1b02799ab7372ff43f6119770c49b0bc3f21bd148820" +dependencies = [ + "ab_glyph", + "approx 0.4.0", "xi-unicode", ] @@ -2066,12 +2152,12 @@ dependencies = [ [[package]] name = "iced_core" version = "0.2.1" -source = "git+https://github.com/Imberflur/iced?branch=text-clone#0a775191abad5787af3aaa302d5599ef12060264" +source = "git+https://github.com/Imberflur/iced#cf514910c2c0db8633377d2719b244e388774cee" [[package]] name = "iced_futures" version = "0.1.2" -source = "git+https://github.com/Imberflur/iced?branch=text-clone#0a775191abad5787af3aaa302d5599ef12060264" +source = "git+https://github.com/Imberflur/iced#cf514910c2c0db8633377d2719b244e388774cee" dependencies = [ "futures 0.3.5", "log", @@ -2081,11 +2167,10 @@ dependencies = [ [[package]] name = "iced_native" version = "0.2.2" -source = "git+https://github.com/Imberflur/iced?branch=text-clone#0a775191abad5787af3aaa302d5599ef12060264" +source = "git+https://github.com/Imberflur/iced#cf514910c2c0db8633377d2719b244e388774cee" dependencies = [ "iced_core", "iced_futures", - "raw-window-handle", "twox-hash", "unicode-segmentation", ] @@ -3112,7 +3197,16 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3" dependencies = [ - "ttf-parser", + "ttf-parser 0.6.2", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb477c7fd2a3a6e04e1dc6ca2e4e9b04f2df702021dc5a5d1cf078c587dc59f7" +dependencies = [ + "ttf-parser 0.8.2", ] [[package]] @@ -3627,7 +3721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270" dependencies = [ "autocfg 1.0.1", - "crossbeam-deque", + "crossbeam-deque 0.7.3", "either", "rayon-core", ] @@ -3639,7 +3733,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf" dependencies = [ "crossbeam-channel 0.4.4", - "crossbeam-deque", + "crossbeam-deque 0.7.3", "crossbeam-utils 0.7.2", "lazy_static", "num_cpus", @@ -3810,8 +3904,8 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f61411055101f7b60ecf1041d87fb74205fb20b0c7a723f07ef39174cf6b4c0" dependencies = [ - "approx", - "crossbeam-deque", + "approx 0.3.2", + "crossbeam-deque 0.7.3", "crossbeam-utils 0.7.2", "linked-hash-map", "num_cpus", @@ -3827,7 +3921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59" dependencies = [ "ab_glyph_rasterizer", - "owned_ttf_parser", + "owned_ttf_parser 0.6.0", ] [[package]] @@ -4590,7 +4684,7 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89" dependencies = [ - "crossbeam-deque", + "crossbeam-deque 0.7.3", "crossbeam-queue 0.2.3", "crossbeam-utils 0.7.2", "futures 0.1.29", @@ -4764,6 +4858,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" +[[package]] +name = "ttf-parser" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d973cfa0e6124166b50a1105a67c85de40bbc625082f35c0f56f84cb1fb0a827" + [[package]] name = "tui" version = "0.10.0" @@ -4788,7 +4888,7 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" dependencies = [ - "rand 0.4.6", + "rand 0.7.3", ] [[package]] @@ -4936,7 +5036,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2657d8704e5e0be82b60157c8dbc71a269273ad766984508fdc54030a0690c4d" dependencies = [ - "approx", + "approx 0.3.2", "num-integer", "num-traits 0.2.12", "rustc_version", @@ -4949,7 +5049,7 @@ name = "vek" version = "0.12.0" source = "git+https://gitlab.com/veloren/vek.git?branch=fix_intrinsics#237a78528b505f34f6dde5dc77db3b642388fe4a" dependencies = [ - "approx", + "approx 0.3.2", "num-integer", "num-traits 0.2.12", "rustc_version", @@ -5301,7 +5401,7 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da" dependencies = [ - "cfg-if", + "cfg-if 0.1.10", "js-sys", "wasm-bindgen", "web-sys", @@ -5695,9 +5795,9 @@ checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" [[package]] name = "xi-unicode" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e71b85d8b1b8bfaf4b5c834187554d201a8cd621c2bbfa33efd41a3ecabd48b2" +checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" [[package]] name = "xml-rs" diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 435d395271..ceab09c555 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -35,9 +35,9 @@ 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/Imberflur/iced", branch = "text-clone"} +iced = {package = "iced_native", git = "https://github.com/Imberflur/iced"} window_clipboard = "0.1.1" -glyph_brush = "0.6.3" +glyph_brush = "0.7.0" # ECS specs = {git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818bad487695c66c43db88050a5"} diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index c3d6b25163..b6032cf29a 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -241,6 +241,7 @@ impl IcedState { .press_image(self.imgs.button_press) .text_color(TEXT_COLOR) .disabled_text_color(DISABLED_TEXT_COLOR); + let buttons = Column::with_children(vec![ self.servers_button.view( i18n.get("common.servers"), @@ -445,7 +446,7 @@ impl<'a> MainMenuUi { .unwrap() .read_to_end(&mut buf) .unwrap(); - glyph_brush::rusttype::Font::from_bytes(buf).unwrap() + ui::ice::Font::try_from_vec(buf).unwrap() }; let mut ice_ui = IcedUi::new(window, ice_font).unwrap(); diff --git a/voxygen/src/ui/ice/cache.rs b/voxygen/src/ui/ice/cache.rs index e061e6c2b5..8e20b3c4ab 100644 --- a/voxygen/src/ui/ice/cache.rs +++ b/voxygen/src/ui/ice/cache.rs @@ -10,12 +10,12 @@ use vek::*; // Multiplied by current window size const GLYPH_CACHE_SIZE: u16 = 1; // Glyph cache tolerances -const SCALE_TOLERANCE: f32 = 0.1; +const SCALE_TOLERANCE: f32 = 0.5; // Note: Changed from 0.1 change back if not decent const POSITION_TOLERANCE: f32 = 0.1; -type GlyphBrush = glyph_brush::GlyphBrush<'static, (Aabr, Aabr)>; +type GlyphBrush = glyph_brush::GlyphBrush<(Aabr, Aabr), ()>; -pub type Font = glyph_brush::rusttype::Font<'static>; +pub type Font = glyph_brush::ab_glyph::FontArc; pub struct Cache { glyph_brush: RefCell, @@ -35,8 +35,8 @@ impl Cache { let glyph_brush = GlyphBrushBuilder::using_font(default_font) .initial_cache_size((glyph_cache_dims.x as u32, glyph_cache_dims.y as u32)) - .gpu_cache_scale_tolerance(SCALE_TOLERANCE) - .gpu_cache_position_tolerance(POSITION_TOLERANCE) + .draw_cache_scale_tolerance(SCALE_TOLERANCE) + .draw_cache_position_tolerance(POSITION_TOLERANCE) .build(); Ok(Self { diff --git a/voxygen/src/ui/ice/renderer/mod.rs b/voxygen/src/ui/ice/renderer/mod.rs index 193e778842..2bc64764c9 100644 --- a/voxygen/src/ui/ice/renderer/mod.rs +++ b/voxygen/src/ui/ice/renderer/mod.rs @@ -1,14 +1,18 @@ mod defaults; +mod primitive; mod style; -mod widgets; +mod widget; pub use defaults::Defaults; pub use style::ButtonStyle; +pub(self) use primitive::Primitive; + use super::{ super::graphic::{self, Graphic, TexId}, cache::Cache, - widget, Font, Rotation, + widget::image, + Font, Rotation, }; use crate::{ render::{ @@ -52,36 +56,6 @@ enum State { Plain, } -pub enum Primitive { - // Allocation :( - Group { - primitives: Vec, - }, - Image { - handle: (widget::image::Handle, Rotation), - bounds: iced::Rectangle, - color: Rgba, - }, - Rectangle { - bounds: iced::Rectangle, - color: Rgba, - }, - Text { - glyphs: Vec<( - glyph_brush::rusttype::PositionedGlyph<'static>, - glyph_brush::Color, - glyph_brush::FontId, - )>, - //size: f32, - bounds: iced::Rectangle, - linear_color: Rgba, - /*font: iced::Font, - *horizontal_alignment: iced::HorizontalAlignment, - *vertical_alignment: iced::VerticalAlignment, */ - }, - Nothing, -} - // Optimization idea inspired by what I think iced wgpu renderer may be doing: // Could have layers of things which don't intersect and thus can be reordered // arbitrarily @@ -107,6 +81,8 @@ pub struct IcedRenderer { p_scale: f32, // Pretend dims :) (i.e. scaled) win_dims: Vec2, + // Scissor for the whole window + window_scissor: Aabr, // Per-frame/update current_state: State, @@ -145,6 +121,7 @@ impl IcedRenderer { align, p_scale, win_dims: scaled_dims, + window_scissor: default_scissor(renderer), start: 0, //current_scissor: default_scissor(renderer), }) @@ -154,7 +131,7 @@ impl IcedRenderer { self.cache.add_graphic(graphic) } - fn image_dims(&self, handle: super::widget::image::Handle) -> (u32, u32) { + fn image_dims(&self, handle: image::Handle) -> (u32, u32) { self .cache .graphic_cache() @@ -218,7 +195,7 @@ impl IcedRenderer { let brush_result = glyph_cache.process_queued( |rect, tex_data| { - let offset = [rect.min.x as u16, rect.min.y as u16]; + let offset = [rect.min[0] as u16, rect.min[1] as u16]; let size = [rect.width() as u16, rect.height() as u16]; let new_data = tex_data @@ -377,6 +354,12 @@ impl IcedRenderer { bounds, color, } => { + let color = srgba_to_linear(color.map(|e| e as f32 / 255.0)); + // Don't draw a transparent image. + if color[3] == 0.0 { + return; + } + let (graphic_id, rotation) = handle; let gl_aabr = self.gl_aabr(bounds); @@ -387,12 +370,6 @@ impl IcedRenderer { _ => {}, } - let color = srgba_to_linear(color.map(|e| e as f32 / 255.0)); - // Don't draw a transparent image. - if color[3] == 0.0 { - return; - } - let resolution = Vec2::new( (gl_aabr.size().w * self.half_res.x).round() as u16, (gl_aabr.size().h * self.half_res.y).round() as u16, @@ -445,10 +422,12 @@ impl IcedRenderer { self.mesh .push_quad(create_ui_quad(gl_aabr, uv_aabr, color, UiMode::Image)); }, - Primitive::Rectangle { bounds, color } => { - let color = srgba_to_linear(color.map(|e| e as f32 / 255.0)); + Primitive::Rectangle { + bounds, + linear_color, + } => { // Don't draw a transparent rectangle. - if color[3] == 0.0 { + if linear_color[3] == 0.0 { return; } @@ -460,7 +439,7 @@ impl IcedRenderer { min: Vec2::zero(), max: Vec2::zero(), }, - color, + linear_color, UiMode::Geometry, )); }, @@ -474,8 +453,6 @@ impl IcedRenderer { } => { self.switch_state(State::Plain); - // TODO: Scissor? - // TODO: makes sure we are not doing all this work for hidden text // e.g. in chat let glyph_cache = self.cache.glyph_cache_mut(); @@ -486,20 +463,21 @@ impl IcedRenderer { // Queue the glyphs to be cached. glyph_cache.queue_pre_positioned( glyphs, + // TODO: glyph_brush should document that these need to be the same length + vec![(); glyph_count], // Since we already passed in `bounds` to position the glyphs some of this // seems redundant... - glyph_brush::rusttype::Rect { - min: glyph_brush::rusttype::Point { - x: bounds.x * self.p_scale, - //y: (self.win_dims.y - bounds.y) * self.p_scale, - y: bounds.y * self.p_scale, - }, - max: glyph_brush::rusttype::Point { - x: (bounds.x + bounds.width) * self.p_scale, - y: (bounds.y + bounds.height) * self.p_scale, - }, + glyph_brush::ab_glyph::Rect { + min: glyph_brush::ab_glyph::point( + bounds.x * self.p_scale, + //(self.win_dims.y - bounds.y) * self.p_scale, + bounds.y * self.p_scale, + ), + max: glyph_brush::ab_glyph::point( + (bounds.x + bounds.width) * self.p_scale, + (bounds.y + bounds.height) * self.p_scale, + ), }, - 0.0, // z (we don't use this) ); // Leave ui and verts blank to fill in when processing cached glyphs @@ -522,6 +500,66 @@ impl IcedRenderer { )); } }, + Primitive::Clip { bounds, content } => { + // Check for a change in the scissor. + let new_scissor = { + // Calculate minimum x and y coordinates while + // flipping y axis (from +down to +uo) and + // moving origin from top-left corner to bottom-left + let min_x = bounds.x; + let min_y = self.win_dims.y - bounds.y; + let intersection = Aabr { + min: Vec2 { + x: (min_x * self.p_scale) as u16, + y: (min_y * self.p_scale) as u16, + }, + max: Vec2 { + x: ((min_x + bounds.width) * self.p_scale) as u16, + y: ((min_y + bounds.height) * self.p_scale) as u16, + }, + } + .intersection(self.window_scissor); + + if intersection.is_valid() { + intersection + } else { + Aabr::new_empty(Vec2::zero()) + } + }; + // Not expecting this case: new_cursor == current_scissor + + // Finish the current command. + // TODO: ensure we never push empty commands (make fields private & debug assert + // in constructors?) + self.draw_commands.push(match self.current_state { + State::Plain => DrawCommand::plain(self.start..self.mesh.vertices().len()), + State::Image(id) => { + DrawCommand::image(self.start..self.mesh.vertices().len(), id) + }, + }); + self.start = self.mesh.vertices().len(); + + self.draw_commands.push(DrawCommand::Scissor(new_scissor)); + + // TODO: support nested clips? + // TODO: if last command is a clip changing back to the default replace it with + // this + + // Renderer child + self.draw_primitive(*content, renderer); + + // Reset scissor + self.draw_commands.push(match self.current_state { + State::Plain => DrawCommand::plain(self.start..self.mesh.vertices().len()), + State::Image(id) => { + DrawCommand::image(self.start..self.mesh.vertices().len(), id) + }, + }); + self.start = self.mesh.vertices().len(); + + self.draw_commands + .push(DrawCommand::Scissor(self.window_scissor)); + }, Primitive::Nothing => {}, } } diff --git a/voxygen/src/ui/ice/renderer/primitive.rs b/voxygen/src/ui/ice/renderer/primitive.rs new file mode 100644 index 0000000000..46734998e1 --- /dev/null +++ b/voxygen/src/ui/ice/renderer/primitive.rs @@ -0,0 +1,31 @@ +use crate::ui::{graphic, ice::widget::image}; + +pub enum Primitive { + // Allocation :( + Group { + primitives: Vec, + }, + Image { + handle: (image::Handle, graphic::Rotation), + bounds: iced::Rectangle, + color: vek::Rgba, + }, + Rectangle { + bounds: iced::Rectangle, + linear_color: vek::Rgba, + }, + Text { + glyphs: Vec, + //size: f32, + bounds: iced::Rectangle, + linear_color: vek::Rgba, + /*font: iced::Font, + *horizontal_alignment: iced::HorizontalAlignment, + *vertical_alignment: iced::VerticalAlignment, */ + }, + Clip { + bounds: iced::Rectangle, + content: Box, + }, + Nothing, +} diff --git a/voxygen/src/ui/ice/renderer/style/button.rs b/voxygen/src/ui/ice/renderer/style/button.rs index c4f3681771..20f02a5264 100644 --- a/voxygen/src/ui/ice/renderer/style/button.rs +++ b/voxygen/src/ui/ice/renderer/style/button.rs @@ -1,4 +1,4 @@ -use super::super::widget::image; +use super::super::super::widget::image; use iced::Color; #[derive(Clone, Copy)] diff --git a/voxygen/src/ui/ice/renderer/widgets/aspect_ratio_container.rs b/voxygen/src/ui/ice/renderer/widget/aspect_ratio_container.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/aspect_ratio_container.rs rename to voxygen/src/ui/ice/renderer/widget/aspect_ratio_container.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/background_container.rs b/voxygen/src/ui/ice/renderer/widget/background_container.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/background_container.rs rename to voxygen/src/ui/ice/renderer/widget/background_container.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/button.rs b/voxygen/src/ui/ice/renderer/widget/button.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/button.rs rename to voxygen/src/ui/ice/renderer/widget/button.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/column.rs b/voxygen/src/ui/ice/renderer/widget/column.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/column.rs rename to voxygen/src/ui/ice/renderer/widget/column.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/compound_graphic.rs b/voxygen/src/ui/ice/renderer/widget/compound_graphic.rs similarity index 83% rename from voxygen/src/ui/ice/renderer/widgets/compound_graphic.rs rename to voxygen/src/ui/ice/renderer/widget/compound_graphic.rs index 3923f19d1b..a3a47af190 100644 --- a/voxygen/src/ui/ice/renderer/widgets/compound_graphic.rs +++ b/voxygen/src/ui/ice/renderer/widget/compound_graphic.rs @@ -2,6 +2,7 @@ use super::super::{ super::{widget::compound_graphic, Rotation}, IcedRenderer, Primitive, }; +use common::util::srgba_to_linear; use compound_graphic::GraphicKind; use iced::{mouse, Rectangle}; @@ -24,7 +25,10 @@ impl compound_graphic::Renderer for IcedRenderer { bounds, color, }, - GraphicKind::Color(color) => Primitive::Rectangle { bounds, color }, + GraphicKind::Color(color) => Primitive::Rectangle { + bounds, + linear_color: srgba_to_linear(color.map(|e| e as f32 * 255.0)), + }, }) .collect(), }, diff --git a/voxygen/src/ui/ice/renderer/widgets/container.rs b/voxygen/src/ui/ice/renderer/widget/container.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/container.rs rename to voxygen/src/ui/ice/renderer/widget/container.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/image.rs b/voxygen/src/ui/ice/renderer/widget/image.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/image.rs rename to voxygen/src/ui/ice/renderer/widget/image.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/mod.rs b/voxygen/src/ui/ice/renderer/widget/mod.rs similarity index 90% rename from voxygen/src/ui/ice/renderer/widgets/mod.rs rename to voxygen/src/ui/ice/renderer/widget/mod.rs index eff22e5c8a..f60c6eb0c5 100644 --- a/voxygen/src/ui/ice/renderer/widgets/mod.rs +++ b/voxygen/src/ui/ice/renderer/widget/mod.rs @@ -8,3 +8,4 @@ mod image; mod row; mod space; mod text; +mod text_input; diff --git a/voxygen/src/ui/ice/renderer/widgets/row.rs b/voxygen/src/ui/ice/renderer/widget/row.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/row.rs rename to voxygen/src/ui/ice/renderer/widget/row.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/space.rs b/voxygen/src/ui/ice/renderer/widget/space.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/space.rs rename to voxygen/src/ui/ice/renderer/widget/space.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/stack.rs b/voxygen/src/ui/ice/renderer/widget/stack.rs similarity index 100% rename from voxygen/src/ui/ice/renderer/widgets/stack.rs rename to voxygen/src/ui/ice/renderer/widget/stack.rs diff --git a/voxygen/src/ui/ice/renderer/widgets/text.rs b/voxygen/src/ui/ice/renderer/widget/text.rs similarity index 84% rename from voxygen/src/ui/ice/renderer/widgets/text.rs rename to voxygen/src/ui/ice/renderer/widget/text.rs index b35d471fa2..d9f6b1629e 100644 --- a/voxygen/src/ui/ice/renderer/widgets/text.rs +++ b/voxygen/src/ui/ice/renderer/widget/text.rs @@ -13,11 +13,15 @@ impl text::Renderer for IcedRenderer { let p_scale = self.p_scale; // TODO: would be nice if the method was mut let section = glyph_brush::Section { - text: content, - scale: glyph_brush::rusttype::Scale::uniform(size as f32 * p_scale), - font_id: font.0, + screen_position: (0.0, 0.0), bounds: (bounds.width * p_scale, bounds.height * p_scale), - ..Default::default() + layout: Default::default(), + text: vec![glyph_brush::Text { + text: content, + scale: (size as f32 * p_scale).into(), + font_id: font.0, + extra: (), + }], }; let maybe_rect = self.cache.glyph_calculator().glyph_bounds(section); @@ -57,30 +61,26 @@ impl text::Renderer for IcedRenderer { let p_scale = self.p_scale; let section = glyph_brush::Section { - text: content, screen_position: (x * p_scale, y * p_scale), bounds: (bounds.width * p_scale, bounds.height * p_scale), - scale: glyph_brush::rusttype::Scale::uniform(size as f32 * p_scale), layout: glyph_brush::Layout::Wrap { line_breaker: Default::default(), h_align, v_align, }, - font_id: font.0, - ..Default::default() + 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) - .map(|positioned_glyph| { - ( - positioned_glyph.clone(), // :/ - [0.0, 0.0, 0.0, 1.0], // Color - font.0, - ) - }) + .cloned() .collect::>(); ( diff --git a/voxygen/src/ui/ice/renderer/widget/text_input.rs b/voxygen/src/ui/ice/renderer/widget/text_input.rs new file mode 100644 index 0000000000..9c6ad3f93f --- /dev/null +++ b/voxygen/src/ui/ice/renderer/widget/text_input.rs @@ -0,0 +1,273 @@ +use super::super::{super::FontId, IcedRenderer, Primitive}; +use glyph_brush::GlyphCruncher; +use iced::{ + mouse, + text_input::{self, cursor}, + Color, Point, Rectangle, +}; + +const CURSOR_WIDTH: f32 = 1.0; +// Extra scroll offset past the cursor +const EXTRA_OFFSET: f32 = 5.0; + +impl text_input::Renderer for IcedRenderer { + type Font = FontId; + type Style = (); + + fn default_size(&self) -> u16 { + // TODO: make configurable + 20 + } + + fn measure_value(&self, value: &str, size: u16, font: Self::Font) -> f32 { + // Using the physical scale might make this cached info usable below? + // Although we also have a position of the screen there so this could be useless + let p_scale = self.p_scale; + + let section = glyph_brush::Section { + screen_position: (0.0, 0.0), + bounds: (f32::INFINITY, f32::INFINITY), + layout: Default::default(), + text: vec![glyph_brush::Text { + text: value, + scale: (size as f32 * p_scale).into(), + font_id: font.0, + extra: (), + }], + }; + + let mut glyph_calculator = self.cache.glyph_calculator(); + let mut width = glyph_calculator + .glyph_bounds(section) + .map_or(0.0, |rect| rect.width() / p_scale); + + // glyph_brush ignores the exterior spaces + // TODO: need better layout lib + let exterior_spaces = value.len() - value.trim().len(); + + if exterior_spaces > 0 { + use glyph_brush::ab_glyph::{Font, ScaleFont}; + // Could cache this if it is slow + let font = glyph_calculator.fonts()[font.0].as_scaled(size as f32); + let space_width = font.h_advance(font.glyph_id(' ')); + width += exterior_spaces as f32 * space_width; + } + + width + } + + fn offset( + &self, + text_bounds: Rectangle, + font: Self::Font, + size: u16, + value: &text_input::Value, + state: &text_input::State, + ) -> f32 { + // Only need to offset if focused with cursor somewhere in the text + if state.is_focused() { + let cursor = state.cursor(); + + let focus_position = match cursor.state(value) { + cursor::State::Index(i) => i, + cursor::State::Selection { end, .. } => end, + }; + + let (_, offset) = measure_cursor_and_scroll_offset( + self, + text_bounds, + value, + size, + focus_position, + font, + ); + + offset + } else { + 0.0 + } + } + + fn draw( + &mut self, + bounds: Rectangle, + text_bounds: Rectangle, + //defaults: &Self::Defaults, No defaults!! + cursor_position: Point, + font: Self::Font, + size: u16, + placeholder: &str, + value: &text_input::Value, + state: &text_input::State, + _style_sheet: &Self::Style, + ) -> Self::Output { + let is_mouse_over = bounds.contains(cursor_position); + + /* + let style = if state.is_focused() { + style.focused() + } else if is_mouse_over { + style.hovered() + } else { + style.active() + }; */ + + let p_scale = self.p_scale; + + // Allocation :( + let text = value.to_string(); + let text = if text.is_empty() { Some(&*text) } else { None }; + + // TODO: background from style, image? + + // TODO: color from style + let color = if text.is_some() { + Color::WHITE + } else { + Color::from_rgba(1.0, 1.0, 1.0, 0.4) + }; + let linear_color = color.into_linear().into(); + + let (cursor_primitive, scroll_offset) = if state.is_focused() { + let cursor = state.cursor(); + + let cursor_and_scroll_offset = |position| { + measure_cursor_and_scroll_offset(self, text_bounds, value, size, position, font) + }; + + let (cursor_primitive, offset) = match cursor.state(value) { + cursor::State::Index(position) => { + let (position, offset) = cursor_and_scroll_offset(position); + ( + Primitive::Rectangle { + bounds: Rectangle { + x: text_bounds.x + position, + y: text_bounds.y, + width: CURSOR_WIDTH / p_scale, + height: text_bounds.height, + }, + linear_color, + }, + offset, + ) + }, + cursor::State::Selection { start, end } => { + let left = start.min(end); + let right = end.max(start); + + let (left_position, left_offset) = cursor_and_scroll_offset(left); + let (right_position, right_offset) = cursor_and_scroll_offset(right); + + let width = right_position - left_position; + + ( + Primitive::Rectangle { + bounds: Rectangle { + x: text_bounds.x + left_position, + y: text_bounds.y, + width, + height: text_bounds.height, + }, + // TODO: selection color from stlye + linear_color: Color::from_rgba(1.0, 0.0, 1.0, 0.2).into_linear().into(), + }, + if end == right { + right_offset + } else { + left_offset + }, + ) + }, + }; + + (Some(cursor_primitive), offset) + } else { + (None, 0.0) + }; + + let section = glyph_brush::Section { + screen_position: ( + text_bounds.x * p_scale + scroll_offset, + text_bounds.center_y() * p_scale, + ), + bounds: (text_bounds.width * p_scale, text_bounds.height * p_scale), + layout: glyph_brush::Layout::SingleLine { + line_breaker: Default::default(), + h_align: glyph_brush::HorizontalAlign::Left, + v_align: glyph_brush::VerticalAlign::Center, + }, + text: vec![glyph_brush::Text { + text: text.unwrap_or(placeholder), + scale: (size as f32 * p_scale).into(), + font_id: font.0, + extra: (), + }], + }; + + let glyphs = self + .cache + .glyph_cache_mut() + .glyphs(section) + .cloned() + .collect::>(); + + let text_primitive = Primitive::Text { + glyphs, + //size: size as f32, + bounds, + linear_color, + /*font, + *horizontal_alignment, + *vertical_alignment, */ + }; + + let primitive = match cursor_primitive { + Some(cursor_primitive) => Primitive::Group { + primitives: vec![cursor_primitive, text_primitive], + }, + None => text_primitive, + }; + + // Probably already computed this somewhere + let text_width = self.measure_value(text.unwrap_or(placeholder), size, font); + + let primitive = if text_width > text_bounds.width { + Primitive::Clip { + bounds: text_bounds, + content: Box::new(primitive), + /* Note: iced_wgpu uses offset here but we can't do that since we pass the text + * to the glyph_brush here */ + } + } else { + primitive + }; + + ( + primitive, + if is_mouse_over { + mouse::Interaction::Text + } else { + mouse::Interaction::default() + }, + ) + } +} + +fn measure_cursor_and_scroll_offset( + renderer: &IcedRenderer, + text_bounds: Rectangle, + value: &text_input::Value, + size: u16, + cursor_index: usize, + font: FontId, +) -> (f32, f32) { + use text_input::Renderer; + + // TODO: so much allocation (fyi .until() allocates) + let text_before_cursor = value.until(cursor_index).to_string(); + + let text_value_width = renderer.measure_value(&text_before_cursor, size, font); + let offset = ((text_value_width + EXTRA_OFFSET) - text_bounds.width).max(0.0); + + (text_value_width, offset) +}