Start rewrite of character selection with iced

This commit is contained in:
Imbris 2020-06-27 01:09:04 -04:00
parent 34f92c7076
commit 545f3bd44b
16 changed files with 811 additions and 42 deletions

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)

Binary file not shown.

BIN
assets/voxygen/element/buttons/x_red_hover.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/voxygen/element/buttons/x_red_press.png (Stored with Git LFS) Normal file

Binary file not shown.

Binary file not shown.

View File

@ -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());
}
}

View 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); }
}

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -5,6 +5,7 @@ mod column;
mod compound_graphic;
mod container;
mod image;
mod overlay;
mod row;
mod scrollable;
mod slider;

View 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)
}
}

View File

@ -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,
};

View 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) }
}