Initial setup to use iced

This commit is contained in:
Imbris 2019-12-15 12:13:52 -05:00 committed by Imbris
parent fb72fc7433
commit bbbede68fc
16 changed files with 1104 additions and 20 deletions

127
Cargo.lock generated
View File

@ -535,6 +535,46 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "clipboard-win"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5123c6b97286809fea9e38d2c9bf530edbcb9fc0d8f8272c28b0c95f067fa92d"
dependencies = [
"error-code",
"str-buf",
"winapi 0.3.9",
]
[[package]]
name = "clipboard_macos"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "145a7f9e9b89453bc0a5e32d166456405d389cea5b578f57f1274b1397588a95"
dependencies = [
"objc",
"objc-foundation",
"objc_id",
]
[[package]]
name = "clipboard_wayland"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926d872adca0fc88173f8b7532c651e29ce67dc97323f4546c1c8af6610937fb"
dependencies = [
"smithay-clipboard 0.5.2",
]
[[package]]
name = "clipboard_x11"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "137cbd60c42327a8d63e710cee5a4d6a1ac41cdc90449ea2c2c63bd5e186290a"
dependencies = [
"xcb",
]
[[package]]
name = "cloudabi"
version = "0.0.3"
@ -708,11 +748,11 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4423d79fed83ebd9ab81ec21fa97144300a961782158287dc9bf7eddac37ff0b"
dependencies = [
"clipboard-win",
"clipboard-win 3.1.1",
"objc",
"objc-foundation",
"objc_id",
"smithay-clipboard",
"smithay-clipboard 0.6.1",
"x11-clipboard",
]
@ -1324,6 +1364,16 @@ dependencies = [
"version_check 0.9.2",
]
[[package]]
name = "error-code"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49c94f66f2d2c5ee8685039e458b4e6c9f13af7c28736baf10ce42966a5ab52"
dependencies = [
"libc",
"str-buf",
]
[[package]]
name = "euc"
version = "0.5.1"
@ -1988,6 +2038,33 @@ dependencies = [
"want",
]
[[package]]
name = "iced_core"
version = "0.2.1"
source = "git+https://github.com/hecrj/iced#f46431600cb61d4e83e0ded1ca79525478436be3"
[[package]]
name = "iced_futures"
version = "0.1.2"
source = "git+https://github.com/hecrj/iced#f46431600cb61d4e83e0ded1ca79525478436be3"
dependencies = [
"futures 0.3.5",
"log",
"wasm-bindgen-futures",
]
[[package]]
name = "iced_native"
version = "0.2.2"
source = "git+https://github.com/hecrj/iced#f46431600cb61d4e83e0ded1ca79525478436be3"
dependencies = [
"iced_core",
"iced_futures",
"num-traits 0.2.12",
"twox-hash",
"unicode-segmentation",
]
[[package]]
name = "ident_case"
version = "1.0.1"
@ -4039,6 +4116,16 @@ dependencies = [
"wayland-protocols 0.28.1",
]
[[package]]
name = "smithay-clipboard"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e9db50a9b272938b767b731a1291f22f407315def4049db93871e8828034d5"
dependencies = [
"smithay-client-toolkit 0.11.0",
"wayland-client 0.27.0",
]
[[package]]
name = "smithay-clipboard"
version = "0.6.1"
@ -4173,6 +4260,12 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
[[package]]
name = "str-buf"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"
[[package]]
name = "string"
version = "0.2.1"
@ -4669,6 +4762,9 @@ name = "twox-hash"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56"
dependencies = [
"rand 0.4.6",
]
[[package]]
name = "tynm"
@ -4989,6 +5085,7 @@ dependencies = [
"glutin",
"guillotiere",
"hashbrown 0.7.2",
"iced_native",
"image",
"inline_tweak",
"itertools",
@ -5014,6 +5111,7 @@ dependencies = [
"veloren-server",
"veloren-voxygen-anim",
"veloren-world",
"window_clipboard",
"winit",
"winres",
]
@ -5171,6 +5269,18 @@ dependencies = [
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da"
dependencies = [
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.68"
@ -5448,6 +5558,19 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "window_clipboard"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b849e24b344ea3535bcda7320b8b7f3560bd2c3692de73153d3c64acc84203e5"
dependencies = [
"clipboard-win 4.0.3",
"clipboard_macos",
"clipboard_wayland",
"clipboard_x11",
"raw-window-handle",
]
[[package]]
name = "winit"
version = "0.22.2"

View File

@ -24,9 +24,6 @@ common = {package = "veloren-common", path = "../common"}
anim = {package = "veloren-voxygen-anim", path = "src/anim", default-features = false}
# Graphics
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"}
gfx = "0.18.2"
gfx_device_gl = {version = "0.16.2", optional = true}
gfx_gl = {version = "0.6.1", optional = true}
@ -34,6 +31,13 @@ glutin = {git = "https://github.com/rust-windowing/glutin.git", rev="63a1ea7d6e6
old_school_gfx_glutin_ext = "0.24"
winit = {version = "0.22.2", features = ["serde"]}
# Ui
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"}
window_clipboard = "0.1.1"
# ECS
specs = {git = "https://github.com/amethyst/specs.git", rev = "7a2e348ab2223818bad487695c66c43db88050a5"}
specs-idvs = {git = "https://gitlab.com/veloren/specs-idvs.git", branch = "specs-git"}

View File

@ -2317,7 +2317,6 @@ impl<'a> Widget for SettingsWindow<'a> {
.global_state
.window
.window()
.window()
.current_monitor()
.unwrap()
.video_modes()

View File

@ -4,11 +4,13 @@ use crate::{
ui::{
self,
fonts::ConrodVoxygenFonts,
ice::IcedUi,
img_ids::{BlankGraphic, ImageGraphic, VoxelGraphic},
Graphic, ImageFrame, Tooltip, Ui,
Graphic, Ui,
},
GlobalState,
};
//ImageFrame, Tooltip,
use common::assets::Asset;
use conrod_core::{
color,
@ -17,6 +19,7 @@ use conrod_core::{
widget::{text_box::Event as TextBoxEvent, Button, Image, List, Rectangle, Text, TextBox},
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, Widget,
};
use iced::Column;
use image::DynamicImage;
use rand::{seq::SliceRandom, thread_rng, Rng};
use std::time::Duration;
@ -122,6 +125,32 @@ image_ids! {
}
}
image_ids_ice! {
struct IcedImgs {
<VoxelGraphic>
v_logo: "voxygen.element.v_logo",
info_frame: "voxygen.element.frames.info_frame_2",
banner: "voxygen.element.frames.banner",
banner_bottom: "voxygen.element.frames.banner_bottom",
<ImageGraphic>
bg: "voxygen.background.bg_main",
banner_top: "voxygen.element.frames.banner_top",
button: "voxygen.element.buttons.button",
button_hover: "voxygen.element.buttons.button_hover",
button_press: "voxygen.element.buttons.button_press",
input_bg_top: "voxygen.element.misc_bg.textbox_top",
input_bg_mid: "voxygen.element.misc_bg.textbox_mid",
input_bg_bot: "voxygen.element.misc_bg.textbox_bot",
disclaimer: "voxygen.element.frames.disclaimer",
<BlankGraphic>
nothing: (),
}
}
rotation_image_ids! {
pub struct ImgsRot {
<ImageGraphic>
@ -158,8 +187,31 @@ pub struct PopupData {
popup_type: PopupType,
}
// No state currently
struct IcedState {
imgs: IcedImgs,
}
pub type Message = Event;
impl IcedState {
pub fn view(&mut self) -> iced::Column<Message, ui::ice::IcedRenderer> {
Column::new().push(ui::ice::Image::new(
(self.imgs.bg, ui::Rotation::None),
500.0,
500.0,
))
}
pub fn update(message: Message) {
match message {
_ => unimplemented!(),
}
}
}
pub struct MainMenuUi {
ui: Ui,
ice_ui: IcedUi,
ice_state: IcedState,
ids: Ids,
imgs: Imgs,
rot_imgs: ImgsRot,
@ -225,8 +277,15 @@ impl<'a> MainMenuUi {
let fonts = ConrodVoxygenFonts::load(&voxygen_i18n.fonts, &mut ui)
.expect("Impossible to load fonts!");
let mut ice_ui = IcedUi::new(window).unwrap();
let ice_state = IcedState {
imgs: IcedImgs::load(&mut ice_ui).expect("Failed to load images"),
};
Self {
ui,
ice_ui,
ice_state,
ids,
imgs,
rot_imgs,
@ -276,7 +335,7 @@ impl<'a> MainMenuUi {
let intro_text = &self.voxygen_i18n.get("main.login_process");
// Tooltip
let _tooltip = Tooltip::new({
/*let _tooltip = Tooltip::new({
// Edge images [t, b, r, l]
// Corner images [tr, tl, br, bl]
let edge = &self.rot_imgs.tt_side;
@ -291,7 +350,7 @@ impl<'a> MainMenuUi {
.title_font_size(self.fonts.cyri.scale(15))
.desc_font_size(self.fonts.cyri.scale(10))
.font_id(self.fonts.cyri.conrod_id)
.desc_text_color(TEXT_COLOR_2);
.desc_text_color(TEXT_COLOR_2);*/
// Background image, Veloren logo, Alpha-Version Label
Image::new(if self.connect {
@ -897,11 +956,18 @@ impl<'a> MainMenuUi {
pub fn handle_event(&mut self, event: ui::Event) { self.ui.handle_event(event); }
pub fn handle_iced_event(&mut self, event: ui::ice::Event) { self.ice_ui.handle_event(event); }
pub fn maintain(&mut self, global_state: &mut GlobalState, dt: Duration) -> Vec<Event> {
let events = self.update_layout(global_state, dt);
self.ui.maintain(global_state.window.renderer_mut(), None);
self.ice_ui
.maintain(self.ice_state.view(), global_state.window.renderer_mut());
events
}
pub fn render(&self, renderer: &mut Renderer) { self.ui.render(renderer, None); }
pub fn render(&self, renderer: &mut Renderer) {
self.ui.render(renderer, None);
self.ice_ui.render(renderer);
}
}

View File

@ -34,6 +34,13 @@ pub fn run(mut global_state: GlobalState, event_loop: EventLoop) {
if let Some(event) = ui::Event::try_from(&event, global_state.window.window()) {
global_state.window.send_event(Event::Ui(event));
}
// iced ui events
// TODO: no clone
if let winit::Event::WindowEvent { event, .. } = event.clone() {
if let Some(event) = ui::ice::window_event(event) {
global_state.window.send_event(Event::IcedUi(event));
}
}
match event {
winit::event::Event::NewEvents(_) => {

View File

@ -0,0 +1,12 @@
// Taken from https://github.com/hecrj/iced/blob/e1438774af809c2951c4c7446638500446c81111/winit/src/clipboard.rs
pub struct Clipboard(window_clipboard::Clipboard);
impl Clipboard {
pub fn new(window: &winit::Window) -> Option<Clipboard> {
window_clipboard::Clipboard::new(window).map(Clipboard).ok()
}
}
impl iced::Clipboard for Clipboard {
fn content(&self) -> Option<String> { self.0.read().ok() }
}

72
voxygen/src/ui/ice/mod.rs Normal file
View File

@ -0,0 +1,72 @@
// tooltip_manager: TooltipManager,
mod clipboard;
mod renderer;
mod widget;
mod winit_conversion;
pub use graphic::{Id, Rotation};
pub use iced::Event;
pub use renderer::IcedRenderer;
pub use widget::image::Image;
pub use winit_conversion::window_event;
use super::graphic::{self, Graphic};
use crate::{render::Renderer, window::Window, Error};
use clipboard::Clipboard;
use iced::{Cache, Element, MouseCursor, Size, UserInterface};
pub struct IcedUi {
renderer: IcedRenderer,
cache: Option<Cache>,
events: Vec<Event>,
clipboard: Clipboard,
}
impl IcedUi {
pub fn new(window: &mut Window) -> Result<Self, Error> {
Ok(Self {
renderer: IcedRenderer::new(window)?,
cache: Some(Cache::new()),
events: Vec::new(),
// TODO: handle None
clipboard: Clipboard::new(window.window()).unwrap(),
})
}
// Add an new graphic that is referencable via the returned Id
pub fn add_graphic(&mut self, graphic: Graphic) -> graphic::Id {
self.renderer.add_graphic(graphic)
}
// TODO: handle scaling here
pub fn handle_event(&mut self, event: Event) { self.events.push(event); }
// TODO: produce root internally???
pub fn maintain<'a, M, E: Into<Element<'a, M, IcedRenderer>>>(
&mut self,
root: E,
renderer: &mut Renderer,
) -> (Vec<M>, MouseCursor) {
// TODO: convert to f32 at source
let window_size = self.renderer.scaled_window_size().map(|e| e as f32);
let mut user_interface = UserInterface::build(
root,
Size::new(window_size.x, window_size.y),
self.cache.take().unwrap(),
&mut self.renderer,
);
let messages =
user_interface.update(self.events.drain(..), Some(&self.clipboard), &self.renderer);
let (primitive, mouse_cursor) = user_interface.draw(&mut self.renderer);
self.renderer.draw(primitive, renderer);
self.cache = Some(user_interface.into_cache());
(messages, mouse_cursor)
}
pub fn render(&self, renderer: &mut Renderer) { self.renderer.render(renderer, None); }
}

View File

@ -0,0 +1,400 @@
mod column;
mod image;
use super::{
super::{
cache::Cache,
graphic::{self, Graphic, TexId},
scale::{Scale, ScaleMode},
},
widget,
};
use crate::{
render::{
create_ui_quad, Consts, DynamicModel, Globals, Mesh, Renderer, UiLocals, UiMode, UiPipeline,
},
window::Window,
Error,
};
//use log::warn;
use std::ops::Range;
use vek::*;
enum DrawKind {
Image(TexId),
// Text and non-textured geometry
Plain,
}
enum DrawCommand {
Draw { kind: DrawKind, verts: Range<usize> },
Scissor(Aabr<u16>),
WorldPos(Option<usize>),
}
impl DrawCommand {
fn image(verts: Range<usize>, id: TexId) -> DrawCommand {
DrawCommand::Draw {
kind: DrawKind::Image(id),
verts,
}
}
fn plain(verts: Range<usize>) -> DrawCommand {
DrawCommand::Draw {
kind: DrawKind::Plain,
verts,
}
}
}
enum State {
Image(TexId),
Plain,
}
pub enum Primitive {
// Allocation :(
Group {
primitives: Vec<Primitive>,
},
Image {
handle: widget::image::Handle,
bounds: iced::Rectangle,
},
}
pub struct IcedRenderer {
//image_map: Map<(Image, Rotation)>,
cache: Cache,
// Model for drawing the ui
model: DynamicModel<UiPipeline>,
// Consts to specify positions of ingame elements (e.g. Nametags)
ingame_locals: Vec<Consts<UiLocals>>,
// Consts for default ui drawing position (ie the interface)
interface_locals: Consts<UiLocals>,
default_globals: Consts<Globals>,
// Window size for updating scaling
//window_resized: Option<Vec2<f64>>,
// Used to delay cache resizing until after current frame is drawn
//need_cache_resize: bool,
// Scaling of the ui
scale: Scale,
half_res: Vec2<f32>,
// Pixel perfection alignment
align: Vec2<f32>,
// Pretend dims :) (i.e. scaled)
win_dims: Vec2<f32>,
// Per-frame/update
current_state: State,
mesh: Mesh<UiPipeline>,
start: usize,
// Draw commands for the next render
draw_commands: Vec<DrawCommand>,
//current_scissor: Aabr<u16>,
}
impl IcedRenderer {
pub fn new(window: &mut Window) -> Result<Self, Error> {
let scale = Scale::new(window, ScaleMode::Absolute(1.0));
// TODO: looks like we can just get this from scale
let win_dims = scale.scaled_window_size().map(|e| e as f32);
let renderer = window.renderer_mut();
let res = renderer.get_resolution();
let half_res = res.map(|e| e as f32 / 2.0);
let align = align(res);
Ok(Self {
cache: Cache::new(renderer)?,
draw_commands: Vec::new(),
model: renderer.create_dynamic_model(100)?,
interface_locals: renderer.create_consts(&[UiLocals::default()])?,
default_globals: renderer.create_consts(&[Globals::default()])?,
ingame_locals: Vec::new(),
//window_resized: None,
//need_cache_resize: false,
mesh: Mesh::new(),
current_state: State::Plain,
scale,
half_res,
align,
win_dims,
start: 0,
//current_scissor: default_scissor(renderer),
})
}
pub fn scaled_window_size(&self) -> Vec2<f64> { self.scale.scaled_window_size() }
pub fn add_graphic(&mut self, graphic: Graphic) -> graphic::Id {
self.cache.add_graphic(graphic)
}
pub fn draw(&mut self, primitive: Primitive, renderer: &mut Renderer) {
/*if self.need_cache_resize {
// Resize graphic cache
self.cache.resize_graphic_cache(renderer).unwrap();
// Resize glyph cache
self.cache.resize_glyph_cache(renderer).unwrap();
self.need_cache_resize = false;
}*/
// Re-use memory
self.draw_commands.clear();
self.mesh.clear();
self.current_state = State::Plain;
self.start = 0;
//self.current_scissor = default_scissor(renderer);
self.draw_primitive(primitive, renderer);
// Enter the final command.
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),
});
// Draw glyph cache (use for debugging).
/*self.draw_commands
.push(DrawCommand::Scissor(default_scissor(renderer)));
start = mesh.vertices().len();
mesh.push_quad(create_ui_quad(
Aabr {
min: (-1.0, -1.0).into(),
max: (1.0, 1.0).into(),
},
Aabr {
min: (0.0, 1.0).into(),
max: (1.0, 0.0).into(),
},
Rgba::new(1.0, 1.0, 1.0, 0.8),
UiMode::Text,
));
self.draw_commands
.push(DrawCommand::plain(start..mesh.vertices().len()));*/
// Create a larger dynamic model if the mesh is larger than the current model
// size.
if self.model.vbuf.len() < self.mesh.vertices().len() {
self.model = renderer
.create_dynamic_model(self.mesh.vertices().len() * 4 / 3)
.unwrap();
}
// Update model with new mesh.
renderer.update_model(&self.model, &self.mesh, 0).unwrap();
// Handle window resizing.
/*if let Some(new_dims) = self.window_resized.take() {
let (old_w, old_h) = self.scale.scaled_window_size().into_tuple();
self.scale.window_resized(new_dims, renderer);
let (w, h) = self.scale.scaled_window_size().into_tuple();
self.ui.handle_event(Input::Resize(w, h));
// Avoid panic in graphic cache when minimizing.
// Avoid resetting cache if window size didn't change
// Somewhat inefficient for elements that won't change size after a window resize
let res = renderer.get_resolution();
self.need_cache_resize = res.x > 0 && res.y > 0 && !(old_w == w && old_h == h);
}*/
}
fn gl_aabr(&self, bounds: iced::Rectangle) -> Aabr<f32> {
/*let (ui_win_w, ui_win_h) = self.win_dims.into_tuple();
let (l, b) = aabr.min.into_tuple();
let (r, t) = aabr.max.into_tuple();
let vx = |x: f64| (x / ui_win_w * 2.0) as f32;
let vy = |y: f64| (y / ui_win_h * 2.0) as f32;
let min = Vec2::new(
((vx(l) * half_res.x + x_align).round() - x_align) / half_res.x,
((vy(b) * half_res.y + y_align).round() - y_align) / half_res.y,
);
let max = Vec2::new(
((vx(r) * half_res.x + x_align).round() - x_align) / half_res.x,
((vy(t) * half_res.y + y_align).round() - y_align) / half_res.y,
);*/
let flipped_y = self.win_dims.y - bounds.y;
let half_win_dims = self.win_dims.map(|e| e / 2.0);
let half_res = self.half_res;
let min = (((Vec2::new(bounds.x, flipped_y - bounds.height) - half_win_dims)
/ half_win_dims
* half_res
+ self.align)
.map(|e| e.round())
- self.align)
/ half_res;
let max = (((Vec2::new(bounds.x + bounds.width, flipped_y) - half_win_dims)
/ half_win_dims
* half_res
+ self.align)
.map(|e| e.round())
- self.align)
/ half_res;
Aabr { min, max }
}
fn draw_primitive(&mut self, primitive: Primitive, renderer: &mut Renderer) {
match primitive {
Primitive::Group { primitives } => {
primitives
.into_iter()
.for_each(|p| self.draw_primitive(p, renderer));
},
Primitive::Image { handle, bounds } => {
let (graphic_id, rotation) = handle;
let gl_aabr = self.gl_aabr(bounds);
let graphic_cache = self.cache.graphic_cache_mut();
match graphic_cache.get_graphic(graphic_id) {
Some(Graphic::Blank) | None => return,
_ => {},
}
// TODO provide color with the image
// let color =
// srgba_to_linear(color.unwrap_or(conrod_core::color::WHITE).to_fsa().
// into());
let color = Rgba::from([1.0, 1.0, 1.0, 1.0]);
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,
);
// Transform the source rectangle into uv coordinate.
// TODO: Make sure this is right.
let source_aabr = {
let (uv_l, uv_r, uv_b, uv_t) = (0.0, 1.0, 0.0, 1.0);
/*match source_rect {
Some(src_rect) => {
let (l, r, b, t) = src_rect.l_r_b_t();
((l / image_w) as f32,
(r / image_w) as f32,
(b / image_h) as f32,
(t / image_h) as f32)
}
None => (0.0, 1.0, 0.0, 1.0),
};*/
Aabr {
min: Vec2::new(uv_l, uv_b),
max: Vec2::new(uv_r, uv_t),
}
};
// Cache graphic at particular resolution.
let (uv_aabr, tex_id) = match graphic_cache.cache_res(
renderer,
graphic_id,
resolution,
source_aabr,
rotation,
) {
// TODO: get dims from graphic_cache (or have it return floats directly)
Some((aabr, tex_id)) => {
let cache_dims = graphic_cache
.get_tex(tex_id)
.get_dimensions()
.map(|e| e as f32);
let min = Vec2::new(aabr.min.x as f32, aabr.max.y as f32) / cache_dims;
let max = Vec2::new(aabr.max.x as f32, aabr.min.y as f32) / cache_dims;
(Aabr { min, max }, tex_id)
},
None => return,
};
match self.current_state {
// Switch to the image state if we are not in it already.
State::Plain => {
self.draw_commands
.push(DrawCommand::plain(self.start..self.mesh.vertices().len()));
self.start = self.mesh.vertices().len();
self.current_state = State::Image(tex_id);
},
// If the image is cached in a different texture switch to the new one
State::Image(id) => {
if id != tex_id {
self.draw_commands.push(DrawCommand::image(
self.start..self.mesh.vertices().len(),
id,
));
self.start = self.mesh.vertices().len();
self.current_state = State::Image(tex_id);
}
},
}
self.mesh
.push_quad(create_ui_quad(gl_aabr, uv_aabr, color, UiMode::Image));
},
}
}
pub fn render(&self, renderer: &mut Renderer, maybe_globals: Option<&Consts<Globals>>) {
let mut scissor = default_scissor(renderer);
let globals = maybe_globals.unwrap_or(&self.default_globals);
let mut locals = &self.interface_locals;
for draw_command in self.draw_commands.iter() {
match draw_command {
DrawCommand::Scissor(new_scissor) => {
scissor = *new_scissor;
},
DrawCommand::WorldPos(index) => {
locals = index.map_or(&self.interface_locals, |i| &self.ingame_locals[i]);
},
DrawCommand::Draw { kind, verts } => {
let tex = match kind {
DrawKind::Image(tex_id) => self.cache.graphic_cache().get_tex(*tex_id),
DrawKind::Plain => self.cache.glyph_cache_tex(),
};
let model = self.model.submodel(verts.clone());
renderer.render_ui_element(&model, tex, scissor, globals, locals);
},
}
}
}
}
// Given the the resolution determines the offset needed to align integer
// offsets from the center of the sceen to pixels
fn align(res: Vec2<u16>) -> Vec2<f32> {
// If the resolution is odd then the center of the screen will be within the
// middle of a pixel so we need to offset by 0.5 pixels to be on the edge of
// a pixel
res.map(|e| (e & 1) as f32 * 0.5)
}
fn default_scissor(renderer: &Renderer) -> Aabr<u16> {
let (screen_w, screen_h) = renderer.get_resolution().map(|e| e as u16).into_tuple();
Aabr {
min: Vec2 { x: 0, y: 0 },
max: Vec2 {
x: screen_w,
y: screen_h,
},
}
}
impl iced::Renderer for IcedRenderer {
// Default styling
type Defaults = ();
// TODO: use graph of primitives to enable diffing???
type Output = (Primitive, iced::MouseCursor);
fn layout<'a, M>(
&mut self,
element: &iced::Element<'a, M, Self>,
limits: &iced::layout::Limits,
) -> iced::layout::Node {
let node = element.layout(self, limits);
// Trim text measurements cache?
node
}
}
// TODO: impl Debugger

View File

@ -0,0 +1,34 @@
use super::{IcedRenderer, Primitive};
use iced::{column, Element, Layout, MouseCursor, Point};
impl column::Renderer for IcedRenderer {
fn draw<M>(
&mut self,
defaults: &Self::Defaults,
content: &[Element<'_, M, Self>],
layout: Layout<'_>,
cursor_position: Point,
) -> Self::Output {
let mut mouse_cursor = MouseCursor::OutOfBounds;
(
Primitive::Group {
primitives: content
.iter()
.zip(layout.children())
.map(|(child, layout)| {
let (primitive, new_mouse_cursor) =
child.draw(self, defaults, layout, cursor_position);
if new_mouse_cursor > mouse_cursor {
mouse_cursor = new_mouse_cursor;
}
primitive
})
.collect(),
},
mouse_cursor,
)
}
}

View File

@ -0,0 +1,14 @@
use super::{super::widget::image, IcedRenderer, Primitive};
use iced::MouseCursor;
impl image::Renderer for IcedRenderer {
fn draw(&mut self, handle: image::Handle, layout: iced::Layout<'_>) -> Self::Output {
(
Primitive::Image {
handle,
bounds: layout.bounds(),
},
MouseCursor::OutOfBounds,
)
}
}

View File

@ -0,0 +1 @@
pub mod image;

View File

@ -0,0 +1,63 @@
use super::super::super::graphic::{self, Rotation};
use iced::{layout, Element, Hasher, Layout, Length, Point, Size, Widget};
use std::hash::Hash;
// TODO: consider iced's approach to images and caching image data
// Also `Graphic` might be a better name for this is it wasn't already in use
// elsewhere
pub type Handle = (graphic::Id, Rotation);
pub struct Image {
handle: Handle,
size: Size,
}
impl Image {
pub fn new(handle: Handle, w: f32, h: f32) -> Self {
let size = Size::new(w, h);
Self { handle, size }
}
}
impl<M, R> Widget<M, R> for Image
where
R: self::Renderer,
{
fn width(&self) -> Length { Length::Fill }
fn height(&self) -> Length { Length::Fill }
fn layout(&self, _renderer: &R, _limits: &layout::Limits) -> layout::Node {
// We don't care about aspect ratios here :p
layout::Node::new(self.size)
// Infinite sizes confusing
//layout::Node::new(limits.resolve(self.size))
}
fn draw(
&self,
renderer: &mut R,
_defaults: &R::Defaults,
layout: Layout<'_>,
_cursor_position: Point,
) -> R::Output {
renderer.draw(self.handle, layout)
}
fn hash_layout(&self, state: &mut Hasher) {
self.size.width.to_bits().hash(state);
self.size.height.to_bits().hash(state);
}
}
pub trait Renderer: iced::Renderer {
fn draw(&mut self, handle: Handle, layout: Layout<'_>) -> Self::Output;
}
impl<'a, M, R> From<Image> for Element<'a, M, R>
where
R: self::Renderer,
{
fn from(image: Image) -> Element<'a, M, R> { Element::new(image) }
}

View File

@ -0,0 +1,271 @@
// Using reference impl: https://github.com/hecrj/iced/blob/e1438774af809c2951c4c7446638500446c81111/winit/src/conversion.rs
use iced::{
input::{
keyboard::{self, KeyCode, ModifiersState},
mouse, ButtonState,
},
window, Event,
};
/// Converts a winit event into an iced event.
pub fn window_event(event: winit::WindowEvent) -> Option<Event> {
use winit::WindowEvent;
match event {
WindowEvent::Resized(new_size) => {
let logical_size: winit::dpi::LogicalSize = new_size;
Some(Event::Window(window::Event::Resized {
width: logical_size.width as u32,
height: logical_size.height as u32,
}))
},
WindowEvent::CursorMoved { position, .. } => {
let position: winit::dpi::LogicalPosition = position;
Some(Event::Mouse(mouse::Event::CursorMoved {
x: position.x as f32,
y: position.y as f32,
}))
},
WindowEvent::MouseInput { button, state, .. } => Some(Event::Mouse(mouse::Event::Input {
button: mouse_button(button),
state: button_state(state),
})),
WindowEvent::MouseWheel { delta, .. } => match delta {
winit::MouseScrollDelta::LineDelta(delta_x, delta_y) => {
Some(Event::Mouse(mouse::Event::WheelScrolled {
delta: mouse::ScrollDelta::Lines {
x: delta_x,
y: delta_y,
},
}))
},
winit::MouseScrollDelta::PixelDelta(position) => {
let position: winit::dpi::LogicalPosition = position;
Some(Event::Mouse(mouse::Event::WheelScrolled {
delta: mouse::ScrollDelta::Pixels {
x: position.x as f32,
y: position.y as f32,
},
}))
},
},
WindowEvent::ReceivedCharacter(c) => {
Some(Event::Keyboard(keyboard::Event::CharacterReceived(c)))
},
WindowEvent::KeyboardInput {
input:
winit::KeyboardInput {
virtual_keycode: Some(virtual_keycode),
state,
modifiers,
..
},
..
} => Some(Event::Keyboard(keyboard::Event::Input {
key_code: key_code(virtual_keycode),
state: button_state(state),
modifiers: modifiers_state(modifiers),
})),
// iced also can use file hovering events but we don't need them right now
_ => None,
}
}
// iced has a function for converting mouse cursors here
/// Converts winit mouse button to iced mouse button
fn mouse_button(mouse_button: winit::MouseButton) -> mouse::Button {
match mouse_button {
winit::MouseButton::Left => mouse::Button::Left,
winit::MouseButton::Right => mouse::Button::Right,
winit::MouseButton::Middle => mouse::Button::Middle,
winit::MouseButton::Other(other) => mouse::Button::Other(other),
}
}
/// Converts winit `ElementState` to an iced button state
fn button_state(element_state: winit::ElementState) -> ButtonState {
match element_state {
winit::ElementState::Pressed => ButtonState::Pressed,
winit::ElementState::Released => ButtonState::Released,
}
}
/// Converts winit `ModifiersState` to iced `ModifiersState`
fn modifiers_state(modifiers: winit::ModifiersState) -> ModifiersState {
ModifiersState {
shift: modifiers.shift,
control: modifiers.ctrl,
alt: modifiers.alt,
logo: modifiers.logo,
}
}
/// Converts winit `VirtualKeyCode` to iced `KeyCode`
fn key_code(virtual_keycode: winit::VirtualKeyCode) -> KeyCode {
match virtual_keycode {
winit::VirtualKeyCode::Key1 => KeyCode::Key1,
winit::VirtualKeyCode::Key2 => KeyCode::Key2,
winit::VirtualKeyCode::Key3 => KeyCode::Key3,
winit::VirtualKeyCode::Key4 => KeyCode::Key4,
winit::VirtualKeyCode::Key5 => KeyCode::Key5,
winit::VirtualKeyCode::Key6 => KeyCode::Key6,
winit::VirtualKeyCode::Key7 => KeyCode::Key7,
winit::VirtualKeyCode::Key8 => KeyCode::Key8,
winit::VirtualKeyCode::Key9 => KeyCode::Key9,
winit::VirtualKeyCode::Key0 => KeyCode::Key0,
winit::VirtualKeyCode::A => KeyCode::A,
winit::VirtualKeyCode::B => KeyCode::B,
winit::VirtualKeyCode::C => KeyCode::C,
winit::VirtualKeyCode::D => KeyCode::D,
winit::VirtualKeyCode::E => KeyCode::E,
winit::VirtualKeyCode::F => KeyCode::F,
winit::VirtualKeyCode::G => KeyCode::G,
winit::VirtualKeyCode::H => KeyCode::H,
winit::VirtualKeyCode::I => KeyCode::I,
winit::VirtualKeyCode::J => KeyCode::J,
winit::VirtualKeyCode::K => KeyCode::K,
winit::VirtualKeyCode::L => KeyCode::L,
winit::VirtualKeyCode::M => KeyCode::M,
winit::VirtualKeyCode::N => KeyCode::N,
winit::VirtualKeyCode::O => KeyCode::O,
winit::VirtualKeyCode::P => KeyCode::P,
winit::VirtualKeyCode::Q => KeyCode::Q,
winit::VirtualKeyCode::R => KeyCode::R,
winit::VirtualKeyCode::S => KeyCode::S,
winit::VirtualKeyCode::T => KeyCode::T,
winit::VirtualKeyCode::U => KeyCode::U,
winit::VirtualKeyCode::V => KeyCode::V,
winit::VirtualKeyCode::W => KeyCode::W,
winit::VirtualKeyCode::X => KeyCode::X,
winit::VirtualKeyCode::Y => KeyCode::Y,
winit::VirtualKeyCode::Z => KeyCode::Z,
winit::VirtualKeyCode::Escape => KeyCode::Escape,
winit::VirtualKeyCode::F1 => KeyCode::F1,
winit::VirtualKeyCode::F2 => KeyCode::F2,
winit::VirtualKeyCode::F3 => KeyCode::F3,
winit::VirtualKeyCode::F4 => KeyCode::F4,
winit::VirtualKeyCode::F5 => KeyCode::F5,
winit::VirtualKeyCode::F6 => KeyCode::F6,
winit::VirtualKeyCode::F7 => KeyCode::F7,
winit::VirtualKeyCode::F8 => KeyCode::F8,
winit::VirtualKeyCode::F9 => KeyCode::F9,
winit::VirtualKeyCode::F10 => KeyCode::F10,
winit::VirtualKeyCode::F11 => KeyCode::F11,
winit::VirtualKeyCode::F12 => KeyCode::F12,
winit::VirtualKeyCode::F13 => KeyCode::F13,
winit::VirtualKeyCode::F14 => KeyCode::F14,
winit::VirtualKeyCode::F15 => KeyCode::F15,
winit::VirtualKeyCode::F16 => KeyCode::F16,
winit::VirtualKeyCode::F17 => KeyCode::F17,
winit::VirtualKeyCode::F18 => KeyCode::F18,
winit::VirtualKeyCode::F19 => KeyCode::F19,
winit::VirtualKeyCode::F20 => KeyCode::F20,
winit::VirtualKeyCode::F21 => KeyCode::F21,
winit::VirtualKeyCode::F22 => KeyCode::F22,
winit::VirtualKeyCode::F23 => KeyCode::F23,
winit::VirtualKeyCode::F24 => KeyCode::F24,
winit::VirtualKeyCode::Snapshot => KeyCode::Snapshot,
winit::VirtualKeyCode::Scroll => KeyCode::Scroll,
winit::VirtualKeyCode::Pause => KeyCode::Pause,
winit::VirtualKeyCode::Insert => KeyCode::Insert,
winit::VirtualKeyCode::Home => KeyCode::Home,
winit::VirtualKeyCode::Delete => KeyCode::Delete,
winit::VirtualKeyCode::End => KeyCode::End,
winit::VirtualKeyCode::PageDown => KeyCode::PageDown,
winit::VirtualKeyCode::PageUp => KeyCode::PageUp,
winit::VirtualKeyCode::Left => KeyCode::Left,
winit::VirtualKeyCode::Up => KeyCode::Up,
winit::VirtualKeyCode::Right => KeyCode::Right,
winit::VirtualKeyCode::Down => KeyCode::Down,
winit::VirtualKeyCode::Back => KeyCode::Backspace,
winit::VirtualKeyCode::Return => KeyCode::Enter,
winit::VirtualKeyCode::Space => KeyCode::Space,
winit::VirtualKeyCode::Compose => KeyCode::Compose,
winit::VirtualKeyCode::Caret => KeyCode::Caret,
winit::VirtualKeyCode::Numlock => KeyCode::Numlock,
winit::VirtualKeyCode::Numpad0 => KeyCode::Numpad0,
winit::VirtualKeyCode::Numpad1 => KeyCode::Numpad1,
winit::VirtualKeyCode::Numpad2 => KeyCode::Numpad2,
winit::VirtualKeyCode::Numpad3 => KeyCode::Numpad3,
winit::VirtualKeyCode::Numpad4 => KeyCode::Numpad4,
winit::VirtualKeyCode::Numpad5 => KeyCode::Numpad5,
winit::VirtualKeyCode::Numpad6 => KeyCode::Numpad6,
winit::VirtualKeyCode::Numpad7 => KeyCode::Numpad7,
winit::VirtualKeyCode::Numpad8 => KeyCode::Numpad8,
winit::VirtualKeyCode::Numpad9 => KeyCode::Numpad9,
winit::VirtualKeyCode::AbntC1 => KeyCode::AbntC1,
winit::VirtualKeyCode::AbntC2 => KeyCode::AbntC2,
winit::VirtualKeyCode::Add => KeyCode::Add,
winit::VirtualKeyCode::Apostrophe => KeyCode::Apostrophe,
winit::VirtualKeyCode::Apps => KeyCode::Apps,
winit::VirtualKeyCode::At => KeyCode::At,
winit::VirtualKeyCode::Ax => KeyCode::Ax,
winit::VirtualKeyCode::Backslash => KeyCode::Backslash,
winit::VirtualKeyCode::Calculator => KeyCode::Calculator,
winit::VirtualKeyCode::Capital => KeyCode::Capital,
winit::VirtualKeyCode::Colon => KeyCode::Colon,
winit::VirtualKeyCode::Comma => KeyCode::Comma,
winit::VirtualKeyCode::Convert => KeyCode::Convert,
winit::VirtualKeyCode::Decimal => KeyCode::Decimal,
winit::VirtualKeyCode::Divide => KeyCode::Divide,
winit::VirtualKeyCode::Equals => KeyCode::Equals,
winit::VirtualKeyCode::Grave => KeyCode::Grave,
winit::VirtualKeyCode::Kana => KeyCode::Kana,
winit::VirtualKeyCode::Kanji => KeyCode::Kanji,
winit::VirtualKeyCode::LAlt => KeyCode::LAlt,
winit::VirtualKeyCode::LBracket => KeyCode::LBracket,
winit::VirtualKeyCode::LControl => KeyCode::LControl,
winit::VirtualKeyCode::LShift => KeyCode::LShift,
winit::VirtualKeyCode::LWin => KeyCode::LWin,
winit::VirtualKeyCode::Mail => KeyCode::Mail,
winit::VirtualKeyCode::MediaSelect => KeyCode::MediaSelect,
winit::VirtualKeyCode::MediaStop => KeyCode::MediaStop,
winit::VirtualKeyCode::Minus => KeyCode::Minus,
winit::VirtualKeyCode::Multiply => KeyCode::Multiply,
winit::VirtualKeyCode::Mute => KeyCode::Mute,
winit::VirtualKeyCode::MyComputer => KeyCode::MyComputer,
winit::VirtualKeyCode::NavigateForward => KeyCode::NavigateForward,
winit::VirtualKeyCode::NavigateBackward => KeyCode::NavigateBackward,
winit::VirtualKeyCode::NextTrack => KeyCode::NextTrack,
winit::VirtualKeyCode::NoConvert => KeyCode::NoConvert,
winit::VirtualKeyCode::NumpadComma => KeyCode::NumpadComma,
winit::VirtualKeyCode::NumpadEnter => KeyCode::NumpadEnter,
winit::VirtualKeyCode::NumpadEquals => KeyCode::NumpadEquals,
winit::VirtualKeyCode::OEM102 => KeyCode::OEM102,
winit::VirtualKeyCode::Period => KeyCode::Period,
winit::VirtualKeyCode::PlayPause => KeyCode::PlayPause,
winit::VirtualKeyCode::Power => KeyCode::Power,
winit::VirtualKeyCode::PrevTrack => KeyCode::PrevTrack,
winit::VirtualKeyCode::RAlt => KeyCode::RAlt,
winit::VirtualKeyCode::RBracket => KeyCode::RBracket,
winit::VirtualKeyCode::RControl => KeyCode::RControl,
winit::VirtualKeyCode::RShift => KeyCode::RShift,
winit::VirtualKeyCode::RWin => KeyCode::RWin,
winit::VirtualKeyCode::Semicolon => KeyCode::Semicolon,
winit::VirtualKeyCode::Slash => KeyCode::Slash,
winit::VirtualKeyCode::Sleep => KeyCode::Sleep,
winit::VirtualKeyCode::Stop => KeyCode::Stop,
winit::VirtualKeyCode::Subtract => KeyCode::Subtract,
winit::VirtualKeyCode::Sysrq => KeyCode::Sysrq,
winit::VirtualKeyCode::Tab => KeyCode::Tab,
winit::VirtualKeyCode::Underline => KeyCode::Underline,
winit::VirtualKeyCode::Unlabeled => KeyCode::Unlabeled,
winit::VirtualKeyCode::VolumeDown => KeyCode::VolumeDown,
winit::VirtualKeyCode::VolumeUp => KeyCode::VolumeUp,
winit::VirtualKeyCode::Wake => KeyCode::Wake,
winit::VirtualKeyCode::WebBack => KeyCode::WebBack,
winit::VirtualKeyCode::WebFavorites => KeyCode::WebFavorites,
winit::VirtualKeyCode::WebForward => KeyCode::WebForward,
winit::VirtualKeyCode::WebHome => KeyCode::WebHome,
winit::VirtualKeyCode::WebRefresh => KeyCode::WebRefresh,
winit::VirtualKeyCode::WebSearch => KeyCode::WebSearch,
winit::VirtualKeyCode::WebStop => KeyCode::WebStop,
winit::VirtualKeyCode::Yen => KeyCode::Yen,
winit::VirtualKeyCode::Copy => KeyCode::Copy,
winit::VirtualKeyCode::Paste => KeyCode::Paste,
winit::VirtualKeyCode::Cut => KeyCode::Cut,
}
}

View File

@ -165,6 +165,25 @@ macro_rules! image_ids {
)*
};
}
#[macro_export]
macro_rules! image_ids_ice {
($($v:vis struct $Ids:ident { $( <$T:ty> $( $name:ident: $specifier:expr ),* $(,)? )* })*) => {
$(
$v struct $Ids {
$($( $v $name: crate::ui::GraphicId, )*)*
}
impl $Ids {
pub fn load(ui: &mut crate::ui::ice::IcedUi) -> Result<Self, common::assets::Error> {
use crate::ui::img_ids::GraphicCreator;
Ok(Self {
$($( $name: ui.add_graphic(<$T as GraphicCreator>::new_graphic($specifier)?), )*)*
})
}
}
)*
};
}
// TODO: combine with the img_ids macro above using a marker for specific fields
// that should be `Rotations` instead of `widget::Id`

View File

@ -7,9 +7,10 @@ mod widgets;
pub mod img_ids;
#[macro_use]
pub mod fonts;
pub mod ice;
pub use event::Event;
pub use graphic::{Graphic, SampleStrat, Transform};
pub use graphic::{Graphic, Id as GraphicId, Rotation, SampleStrat, Transform};
pub use scale::{Scale, ScaleMode};
pub use widgets::{
image_frame::ImageFrame,
@ -36,7 +37,7 @@ use common::{assets, span, util::srgba_to_linear};
use conrod_core::{
event::Input,
graph::{self, Graph},
image::{self, Map},
image::{Id as ImageId, Map},
input::{touch::Touch, Motion, Widget},
render::{Primitive, PrimitiveKind},
text::{self, font},
@ -187,7 +188,7 @@ impl Ui {
// Get a copy of Scale
pub fn scale(&self) -> Scale { self.scale }
pub fn add_graphic(&mut self, graphic: Graphic) -> image::Id {
pub fn add_graphic(&mut self, graphic: Graphic) -> ImageId {
self.image_map
.insert((self.cache.add_graphic(graphic), Rotation::None))
}
@ -212,7 +213,7 @@ impl Ui {
}
}
pub fn replace_graphic(&mut self, id: image::Id, graphic: Graphic) {
pub fn replace_graphic(&mut self, id: ImageId, graphic: Graphic) {
let graphic_id = if let Some((graphic_id, _)) = self.image_map.get(&id) {
*graphic_id
} else {

View File

@ -282,6 +282,8 @@ pub enum Event {
InputUpdate(GameInput, bool),
/// Event that the ui uses.
Ui(ui::Event),
/// Event that the iced ui uses.
IcedUi(ui::ice::Event),
/// The view distance has changed.
ViewDistanceChanged(u32),
/// Game settings have changed.
@ -622,12 +624,6 @@ impl Window {
Ok((this, event_loop))
}
pub fn window(
&self,
) -> &glutin::ContextWrapper<glutin::PossiblyCurrent, winit::window::Window> {
&self.window
}
pub fn renderer(&self) -> &Renderer { &self.renderer }
pub fn renderer_mut(&mut self) -> &mut Renderer { &mut self.renderer }
@ -1377,6 +1373,8 @@ impl Window {
pub fn set_keybinding_mode(&mut self, game_input: GameInput) {
self.remapping_keybindings = Some(game_input);
}
pub fn window(&self) -> &winit::Window { self.window.window() }
}
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug, Serialize, Deserialize)]