Add ability to position tuples of widgets ingame

Former-commit-id: e2958a37e7062132d6a7b6d5ad61bd4002768532
This commit is contained in:
Imbris 2019-05-19 10:15:50 -04:00
parent b828bffba9
commit 93ec66a8b1
4 changed files with 294 additions and 81 deletions

View File

@ -25,7 +25,7 @@ use small_window::{SmallWindow, SmallWindowType};
use crate::{
render::{Consts, Globals, Renderer},
settings::{ControlSettings, Settings},
ui::{Ingame, ScaleMode, Ui},
ui::{Ingame, Ingameable, ScaleMode, Ui},
window::{Event as WinEvent, Key, Window},
GlobalState,
};
@ -287,27 +287,24 @@ impl Hud {
})
.enumerate()
{
Ingame::from_primitive(
pos + Vec3::new(0.0, 0.0, 3.0),
Text::new(&name)
.font_size(15)
.color(Color::Rgba(1.0, 1.0, 1.0, 1.0)),
)
.resolution(50.0)
.set(self.ids.name_tags[i], ui_widgets);
Text::new(&name)
.font_size(20)
.color(Color::Rgba(1.0, 1.0, 1.0, 1.0))
.x_y(0.0, 0.0)
.position_ingame(pos + Vec3::new(0.0, 0.0, 3.0))
.resolution(100.0)
.set(self.ids.name_tags[i], ui_widgets);
}
}
// test
Ingame::from_primitive(
[0.0, 25.0, 25.0].into(),
//Rectangle::fill_with([1.0, 1.0], Color::Rgba(0.2, 0.0, 0.4, 1.0)),
Text::new("Squarefection")
.font_size(20)
.color(TEXT_COLOR)
.font_id(self.fonts.opensans),
)
.resolution(40.0)
.set(self.ids.temp, ui_widgets);
Text::new("Squarefection")
.font_size(20)
.color(TEXT_COLOR)
.font_id(self.fonts.opensans)
.x_y(0.0, 0.0)
.position_ingame([0.0, 25.0, 25.0].into())
.resolution(40.0)
.set(self.ids.temp, ui_widgets);
// Display debug window.
if self.show.debug {

View File

@ -6,7 +6,6 @@ pub struct Event(pub Input);
impl Event {
pub fn try_from(event: glutin::Event, window: &glutin::GlWindow) -> Option<Self> {
use conrod_winit::*;
use winit;
// A wrapper around the winit window that allows us to implement the trait necessary for
// enabling the winit <-> conrod conversion functions.
struct WindowRef<'a>(&'a winit::Window);

View File

@ -12,7 +12,11 @@ mod font_ids;
pub use event::Event;
pub use graphic::Graphic;
pub use scale::ScaleMode;
pub use widgets::{image_slider::ImageSlider, ingame::Ingame, toggle_button::ToggleButton};
pub use widgets::{
image_slider::ImageSlider,
ingame::{Ingame, Ingameable},
toggle_button::ToggleButton,
};
use crate::{
render::{
@ -294,8 +298,12 @@ impl Ui {
// Push new position command
self.draw_commands.push(DrawCommand::WorldPos(None));
}
Some((n, res)) => in_world = Some((n - 1, res)),
None => (),
Some((n, res)) => match kind {
// Other types don't need to be drawn in the game
PrimitiveKind::Other(_) => (),
_ => in_world = Some((n - 1, res)),
},
None => {}
}
// Functions for converting for conrod scalar coords to GL vertex coords (-1.0 to 1.0).
@ -500,11 +508,11 @@ impl Ui {
PrimitiveKind::Other(container) => {
if container.type_id == std::any::TypeId::of::<widgets::ingame::State>() {
// Retrieve world position
let (pos, res) = container
let parameters = container
.state_and_style::<widgets::ingame::State, widgets::ingame::Style>()
.unwrap()
.state
.pos_res();
.parameters;
// Finish current state
self.draw_commands.push(match current_state {
State::Plain => DrawCommand::plain(start..mesh.vertices().len()),
@ -513,10 +521,10 @@ impl Ui {
start = mesh.vertices().len();
// Push new position command
self.draw_commands.push(DrawCommand::WorldPos(Some(
renderer.create_consts(&[pos.into()]).unwrap(),
renderer.create_consts(&[parameters.pos.into()]).unwrap(),
)));
in_world = Some((1, res));
in_world = Some((parameters.num, parameters.res));
p_scale_factor = 1.0;
}
}

View File

@ -1,82 +1,232 @@
use conrod_core::{
builder_methods, image,
position::Dimension,
widget::{self, button},
widget_ids, Color, Position, Positionable, Rect, Sizeable, Ui, Widget, WidgetCommon,
widget::{self, button, Id},
widget_ids, Color, Position, Positionable, Rect, Sizeable, Ui, UiCell, Widget, WidgetCommon,
};
use std::slice;
use vek::*;
#[derive(Clone, WidgetCommon)]
pub struct Ingame<W>
where
W: Widget,
{
pub struct Ingame<W> {
#[conrod(common_builder)]
common: widget::CommonBuilder,
widget: W,
pos: Vec3<f32>,
parameters: IngameParameters,
}
pub trait Ingameable: Sized {
type Event;
fn prim_count(&self) -> usize;
fn set_ingame(self, ids: Ids, parent_id: Id, ui: &mut UiCell) -> Self::Event;
fn init_ids(mut id_gen: widget::id::Generator) -> Ids;
fn position_ingame(self, pos: Vec3<f32>) -> Ingame<Self> {
Ingame::new(pos, self)
}
}
// Note this is not responsible for the positioning
// Only call this directly if using IngameAnchor
pub fn set_ingame<W: Widget>(widget: W, parent_id: Id, id: Id, ui: &mut UiCell) -> W::Event {
widget
// should pass focus to the window if these are clicked
// (they are not displayed where conrod thinks they are)
.graphics_for(ui.window)
//.parent(id) // is this needed
.set(id, ui)
}
pub trait PrimitiveMarker {}
impl PrimitiveMarker for widget::Line {}
impl PrimitiveMarker for widget::Image {}
impl<I> PrimitiveMarker for widget::PointPath<I> {}
impl PrimitiveMarker for widget::Circle {}
impl<S> PrimitiveMarker for widget::Oval<S> {}
impl<I> PrimitiveMarker for widget::Polygon<I> {}
impl PrimitiveMarker for widget::Rectangle {}
impl<S, I> PrimitiveMarker for widget::Triangles<S, I> {}
impl<'a> PrimitiveMarker for widget::Text<'a> {}
impl<P> Ingameable for P
where
P: Widget + PrimitiveMarker,
{
type Event = P::Event;
fn prim_count(&self) -> usize {
1
}
fn set_ingame(self, ids: Ids, parent_id: Id, ui: &mut UiCell) -> Self::Event {
let id = ids.one().unwrap();
set_ingame(self, parent_id, id, ui)
}
fn init_ids(mut id_gen: widget::id::Generator) -> Ids {
Ids::One(id_gen.next())
}
}
trait IngameWidget: Ingameable + Widget {}
impl<T> IngameWidget for T where T: Ingameable + Widget {}
impl<W, E> Ingameable for (W, E)
where
W: IngameWidget,
E: IngameWidget,
{
type Event = (<W as Widget>::Event, <E as Widget>::Event);
fn prim_count(&self) -> usize {
self.0.prim_count() + self.1.prim_count()
}
fn set_ingame(self, ids: Ids, parent_id: Id, ui: &mut UiCell) -> Self::Event {
let (w1, w2) = self;
let [id1, id2] = ids.two().unwrap();
(
set_ingame(w1, parent_id, id1, ui),
set_ingame(w2, parent_id, id2, ui),
)
}
fn init_ids(mut id_gen: widget::id::Generator) -> Ids {
Ids::Two([id_gen.next(), id_gen.next()])
}
}
impl<W, E, R> Ingameable for (W, E, R)
where
W: IngameWidget,
E: IngameWidget,
R: IngameWidget,
{
type Event = (
<W as Widget>::Event,
<E as Widget>::Event,
<R as Widget>::Event,
);
fn prim_count(&self) -> usize {
self.0.prim_count() + self.1.prim_count() + self.2.prim_count()
}
fn set_ingame(self, ids: Ids, parent_id: Id, ui: &mut UiCell) -> Self::Event {
let (w1, w2, w3) = self;
let ids = ids.three().unwrap();
(
set_ingame(w1, parent_id, ids[0], ui),
set_ingame(w2, parent_id, ids[1], ui),
set_ingame(w3, parent_id, ids[2], ui),
)
}
fn init_ids(mut id_gen: widget::id::Generator) -> Ids {
Ids::Three([id_gen.next(), id_gen.next(), id_gen.next()])
}
}
impl<W, E, R, T> Ingameable for (W, E, R, T)
where
W: IngameWidget,
E: IngameWidget,
R: IngameWidget,
T: IngameWidget,
{
type Event = (
<W as Widget>::Event,
<E as Widget>::Event,
<R as Widget>::Event,
<T as Widget>::Event,
);
fn prim_count(&self) -> usize {
self.0.prim_count() + self.1.prim_count() + self.2.prim_count() + self.3.prim_count()
}
fn set_ingame(self, ids: Ids, parent_id: Id, ui: &mut UiCell) -> Self::Event {
let (w1, w2, w3, w4) = self;
let ids = ids.four().unwrap();
(
set_ingame(w1, parent_id, ids[0], ui),
set_ingame(w2, parent_id, ids[1], ui),
set_ingame(w3, parent_id, ids[2], ui),
set_ingame(w4, parent_id, ids[3], ui),
)
}
fn init_ids(mut id_gen: widget::id::Generator) -> Ids {
Ids::Four([id_gen.next(), id_gen.next(), id_gen.next(), id_gen.next()])
}
}
#[derive(Clone, Copy)]
enum Ids {
None,
One(Id),
Two([Id; 2]),
Three([Id; 3]),
Four([Id; 4]),
}
impl Ids {
fn one(self) -> Option<Id> {
match self {
Ids::One(id) => Some(id),
_ => None,
}
}
fn two(self) -> Option<[Id; 2]> {
match self {
Ids::Two(ids) => Some(ids),
_ => None,
}
}
fn three(self) -> Option<[Id; 3]> {
match self {
Ids::Three(ids) => Some(ids),
_ => None,
}
}
fn four(self) -> Option<[Id; 4]> {
match self {
Ids::Four(ids) => Some(ids),
_ => None,
}
}
}
#[derive(Copy, Clone, PartialEq)]
pub struct IngameParameters {
// Number of primitive widgets to position in the game at the specified position
// Note this could be more than the number of widgets in the widgets field since widgets can contain widgets
pub num: usize,
pub pos: Vec3<f32>,
// Number of pixels per 1 unit in world coordinates (ie a voxel)
// Used for widgets that are rasterized before being sent to the gpu (text & images)
// Potentially make this autmatic based on distance to camera?
res: f32,
}
// TODO: add convenience function to this trait
pub trait Primitive {}
impl Primitive for widget::Line {}
impl Primitive for widget::Image {}
impl<I> Primitive for widget::PointPath<I> {}
impl Primitive for widget::Circle {}
impl<S> Primitive for widget::Oval<S> {}
impl<I> Primitive for widget::Polygon<I> {}
impl Primitive for widget::Rectangle {}
impl<S, I> Primitive for widget::Triangles<S, I> {}
impl<'a> Primitive for widget::Text<'a> {}
widget_ids! {
struct Ids {
prim,
}
pub res: f32,
}
pub struct State {
ids: Ids,
pos: Vec3<f32>,
res: f32,
}
impl State {
// retrieve the postion and resolution as a tuple
pub fn pos_res(&self) -> (Vec3<f32>, f32) {
(self.pos, self.res)
}
pub parameters: IngameParameters,
}
pub type Style = ();
impl<W: Widget + Primitive> Ingame<W> {
pub fn from_primitive(pos: Vec3<f32>, widget: W) -> Self {
impl<W: Ingameable> Ingame<W> {
pub fn new(pos: Vec3<f32>, widget: W) -> Self {
Self {
common: widget::CommonBuilder::default(),
pos,
parameters: IngameParameters {
num: widget.prim_count(),
pos,
res: 1.0,
},
widget,
res: 1.0,
}
}
builder_methods! {
pub resolution { res = f32 }
pub resolution { parameters.res = f32 }
}
}
impl<W: Widget> Widget for Ingame<W> {
impl<W: Ingameable> Widget for Ingame<W> {
type State = State;
type Style = Style;
type Event = ();
type Event = W::Event;
fn init_state(&self, id_gen: widget::id::Generator) -> Self::State {
fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
State {
ids: Ids::new(id_gen),
pos: Vec3::default(),
res: 1.0,
ids: W::init_ids(id_gen),
parameters: self.parameters,
}
}
@ -87,22 +237,17 @@ impl<W: Widget> Widget for Ingame<W> {
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { id, state, ui, .. } = args;
let Ingame {
widget, pos, res, ..
widget, parameters, ..
} = self;
// Update pos if it has changed
if state.pos != pos || state.res != res {
if state.parameters != parameters {
state.update(|s| {
s.pos = pos;
s.res = res;
s.parameters = parameters;
});
}
widget
.graphics_for(ui.window)
.x_y(0.0, 0.0)
.parent(id) // is this needed
.set(state.ids.prim, ui);
widget.set_ingame(state.ids, id, ui)
}
fn default_x_position(&self, ui: &Ui) -> Position {
@ -118,3 +263,67 @@ impl<W: Widget> Widget for Ingame<W> {
Dimension::Absolute(1.0)
}
}
// Use this if you have multiple widgets that you want to place at the same spot in-game
// but don't want to create a new custom widget to contain them both
// Note: widgets must be set immediately after settings this
// Note: remove this if it ends up unused
#[derive(Clone, WidgetCommon)]
pub struct IngameAnchor {
#[conrod(common_builder)]
common: widget::CommonBuilder,
parameters: IngameParameters,
}
impl IngameAnchor {
pub fn new(pos: Vec3<f32>) -> Self {
IngameAnchor {
common: widget::CommonBuilder::default(),
parameters: IngameParameters {
num: 0,
pos,
res: 1.0,
},
}
}
pub fn for_widget(mut self, widget: impl Ingameable) -> Self {
self.parameters.num += widget.prim_count();
self
}
pub fn for_widgets(mut self, widget: impl Ingameable, n: usize) -> Self {
self.parameters.num += n * widget.prim_count();
self
}
pub fn for_prims(mut self, num: usize) -> Self {
self.parameters.num += num;
self
}
}
impl Widget for IngameAnchor {
type State = State;
type Style = Style;
type Event = ();
fn init_state(&self, _: widget::id::Generator) -> Self::State {
State {
ids: Ids::None,
parameters: self.parameters,
}
}
fn style(&self) -> Self::Style {
()
}
fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
let widget::UpdateArgs { id, state, ui, .. } = args;
let IngameAnchor { parameters, .. } = self;
// Update pos if it has changed
if state.parameters != parameters {
state.update(|s| {
s.parameters = parameters;
});
}
}
}