Skip to main content

veloren_voxygen/ui/widgets/
ingame.rs

1use conrod_core::{Position, Sizeable, Ui, Widget, WidgetCommon, position::Dimension, widget};
2use vek::*;
3
4/// Extension trait for positioning a widget at a 3D point in the game world.
5pub trait Ingameable: Widget + Sized {
6    /// Positions a widget as if it was at `pos` in the 3D game world.
7    ///
8    /// Wraps the widget in a parent `Ingame` widget which collaborates with
9    /// custom code in our UI renderering logic to pass the positioning
10    /// information through.
11    ///
12    /// Note, the widget size will not be scaled based on distance for
13    /// performance and stylistic purposes.
14    ///
15    /// Note, widgets set in the `Widget::update` impl of `self` should not use
16    /// `position_ingame` themselves. I.E. nested usage is not supported.
17    fn position_ingame(self, pos: Vec3<f32>) -> Ingame<Self> { Ingame::new(pos, self) }
18}
19
20impl<W: Widget> Ingameable for W {}
21
22// All ingame widgets are now fixed scale
23#[derive(Copy, Clone, PartialEq)]
24pub struct IngameParameters {
25    pub pos: Vec3<f32>,
26    pub dims: Vec2<f32>,
27}
28
29/// Positions wrapped `widget` in the 3D game world at `pos`.
30///
31/// The position on screen will depend on the 3D camera.
32///
33/// This uses some custom logic in the UI renderer to detect when this widget is
34/// encountered and extract `IngameParameters` which are applied until the
35/// `IngameEndMarker` widget is encountered. This relies on the details of
36/// `conrod` to ensure that `IngameEndMarker` will be encountered after
37/// all primitives produced by the wrapped widget.
38#[derive(Clone, WidgetCommon)]
39pub struct Ingame<W> {
40    #[conrod(common_builder)]
41    common: widget::CommonBuilder,
42    widget: W,
43    pos: Vec3<f32>,
44}
45
46pub struct State {
47    inner_id: widget::Id,
48    end_id: widget::Id,
49    pub(in crate::ui) parameters: IngameParameters,
50}
51
52impl<W: Ingameable> Ingame<W> {
53    fn new(pos: Vec3<f32>, widget: W) -> Self {
54        Self {
55            common: widget::CommonBuilder::default(),
56            pos,
57            widget,
58        }
59    }
60}
61
62impl<W: Ingameable> Widget for Ingame<W> {
63    type Event = W::Event;
64    type State = State;
65    type Style = ();
66
67    fn init_state(&self, mut id_gen: widget::id::Generator) -> Self::State {
68        State {
69            inner_id: id_gen.next(),
70            end_id: id_gen.next(),
71            parameters: IngameParameters {
72                pos: self.pos,
73                dims: Vec2::zero(),
74            },
75        }
76    }
77
78    fn style(&self) -> Self::Style {}
79
80    fn update(self, args: widget::UpdateArgs<Self>) -> Self::Event {
81        let widget::UpdateArgs { state, ui, .. } = args;
82        let Ingame { widget, pos, .. } = self;
83
84        let parameters = IngameParameters {
85            pos,
86            dims: Vec2::<f64>::from(widget.get_wh(ui).unwrap_or([1.0, 1.0])).map(|e| e as f32),
87        };
88
89        // Update parameters if it has changed
90        if state.parameters != parameters {
91            state.update(|s| {
92                s.parameters = parameters;
93            });
94        }
95
96        let event = widget
97            // should pass focus to the window if these are clicked
98            // (they are not displayed where conrod thinks they are)
99            .graphics_for(ui.window)
100            .set(state.inner_id, ui);
101        IngameEndMarker::default().set(state.end_id, ui);
102        event
103    }
104
105    fn default_x_position(&self, _: &Ui) -> Position { Position::Absolute(0.0) }
106
107    fn default_y_position(&self, _: &Ui) -> Position { Position::Absolute(0.0) }
108
109    fn default_x_dimension(&self, _: &Ui) -> Dimension { Dimension::Absolute(0.0) }
110
111    fn default_y_dimension(&self, _: &Ui) -> Dimension { Dimension::Absolute(0.0) }
112}
113
114// This must be a unique type to detect it in the rendering primitives.
115pub(in crate::ui) struct IngameEndMarkerState;
116
117#[derive(WidgetCommon, Default)]
118struct IngameEndMarker {
119    #[conrod(common_builder)]
120    common: widget::CommonBuilder,
121}
122
123impl Widget for IngameEndMarker {
124    type Event = ();
125    type State = IngameEndMarkerState;
126    type Style = ();
127
128    fn init_state(&self, _id_gen: widget::id::Generator) -> Self::State { IngameEndMarkerState }
129
130    fn style(&self) -> Self::Style {}
131
132    fn update(self, _args: widget::UpdateArgs<Self>) -> Self::Event {}
133
134    fn default_x_position(&self, _: &Ui) -> Position { Position::Absolute(0.0) }
135
136    fn default_y_position(&self, _: &Ui) -> Position { Position::Absolute(0.0) }
137
138    fn default_x_dimension(&self, _: &Ui) -> Dimension { Dimension::Absolute(0.0) }
139
140    fn default_y_dimension(&self, _: &Ui) -> Dimension { Dimension::Absolute(0.0) }
141}