Skip to content

Commit

Permalink
Use ListenableCell to relieve AllIsCubesAppState of needing to know a…
Browse files Browse the repository at this point in the history
…bout the crosshair.
  • Loading branch information
kpreid committed Apr 11, 2021
1 parent 7bcf453 commit 4c8c68d
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 32 deletions.
10 changes: 5 additions & 5 deletions all-is-cubes/src/apps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,16 @@ impl AllIsCubesAppState {
// TODO: better error handling
.expect("Failure while constructing template");

let input_processor = InputProcessor::new();

let mut new_self = Self {
ui: Vui::new(&input_processor),

frame_clock: FrameClock::new(),
input_processor: InputProcessor::new(),
input_processor,
graphics_options: ListenableCell::new(GraphicsOptions::default()),
game_character: game_universe.get_default_character(),
game_universe,
ui: Vui::new(),
ui_dirty: DirtyFlag::new(true),
cursor_result: None,
};
Expand Down Expand Up @@ -124,9 +127,6 @@ impl AllIsCubesAppState {
.set_toolbar(&character.inventory().slots, &character.selected_slots())
.unwrap();
}
self.ui
.set_crosshair_visible(self.input_processor.mouselook_mode)
.unwrap(); // TODO: ui should have internal error handling
}

/// Call this once per frame to update the cursor raycast.
Expand Down
27 changes: 17 additions & 10 deletions all-is-cubes/src/apps/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::time::Duration;

use crate::camera::Viewport;
use crate::character::Character;
use crate::listen::{ListenableCell, ListenableSource};
use crate::math::FreeCoordinate;

/// Parse input events, particularly key-down/up pairs, into character control and such.
Expand All @@ -21,7 +22,7 @@ use crate::math::FreeCoordinate;
/// of input on the relevant [`Character`].
/// 3. The game loop should call [`InputProcessor::step`] to apply the effects of time
/// on the input processor.
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct InputProcessor {
/// All [`Key`]s currently pressed.
keys_held: HashSet<Key>,
Expand All @@ -33,9 +34,10 @@ pub struct InputProcessor {
/// once per press rather than while held.
command_buffer: Vec<Key>,

/// Do we *want* pointer lock for mouselook? Controlled by UI.
// TODO: Stop making this public directly
pub(super) mouselook_mode: bool,
/// Do we *want* pointer lock for mouselook?
///
/// This is listenable so that the UI can react to this state.
mouselook_mode: ListenableCell<bool>,
/// Do we *have* pointer lock for mouselook? Reported by calling input implementation.
has_pointer_lock: bool,

Expand All @@ -57,7 +59,7 @@ impl InputProcessor {
keys_held: HashSet::new(),
momentary_timeout: HashMap::new(),
command_buffer: Vec::new(),
mouselook_mode: false, // TODO: might want a parameter
mouselook_mode: ListenableCell::new(false), // TODO: might want a parameter
has_pointer_lock: false,
mouselook_buffer: Vector2::zero(),
mouse_ndc_position: Some(Point2::origin()),
Expand Down Expand Up @@ -135,7 +137,7 @@ impl InputProcessor {
self.keys_held.clear();
self.momentary_timeout.clear();

self.mouselook_mode = false;
self.mouselook_mode.set(false);
}
}

Expand All @@ -144,7 +146,7 @@ impl InputProcessor {
/// may lack focus, the application may lack permission, etc.; use
/// [`InputProcessor::has_pointer_lock`] to report that state.
pub fn wants_pointer_lock(&self) -> bool {
self.mouselook_mode
*self.mouselook_mode.get()
}

/// Use this method to report whether mouse mouse pointer lock/capture/disable is
Expand Down Expand Up @@ -265,8 +267,9 @@ impl InputProcessor {
for key in self.command_buffer.drain(..) {
match key {
Key::Character('l') => {
self.mouselook_mode = !self.mouselook_mode;
if self.mouselook_mode {
let new_state = !*self.mouselook_mode.get();
self.mouselook_mode.set(new_state);
if new_state {
// Clear delta tracking just in case
self.mouse_previous_pixel_position = None;
}
Expand All @@ -281,13 +284,17 @@ impl InputProcessor {
}
}

pub fn mouselook_mode(&self) -> ListenableSource<bool> {
self.mouselook_mode.as_source()
}

/// Returns the position which should be used for click/cursor raycasting.
/// This is not necessarily equal to the tracked mouse position.
///
/// Returns [`None`] if the mouse position is out of bounds, the window has lost
/// focus, or similar conditions under which no cursor should be shown.
pub fn cursor_ndc_position(&self) -> Option<Point2<FreeCoordinate>> {
if self.mouselook_mode {
if *self.mouselook_mode.get() {
Some(Point2::origin())
} else {
self.mouse_ndc_position
Expand Down
47 changes: 30 additions & 17 deletions all-is-cubes/src/vui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ use ordered_float::NotNan;
use std::borrow::Cow;
use std::time::Duration;

use crate::apps::InputProcessor;
use crate::block::{Block, AIR};
use crate::camera::{FogOption, GraphicsOptions};
use crate::content::palette;
use crate::drawing::VoxelBrush;
use crate::listen::ListenableSource;
use crate::math::{FreeCoordinate, GridMatrix};
use crate::space::{SetCubeError, Space};
use crate::tools::Tool;
Expand All @@ -41,10 +43,15 @@ pub(crate) struct Vui {

/// None if the tooltip is blanked
tooltip_age: Option<Duration>,

mouselook_mode: ListenableSource<bool>,
}

impl Vui {
pub fn new() -> Self {
/// `input_processor` is the `InputProcessor` whose state may be reflected on the HUD.
/// TODO: Reduce coupling, perhaps by passing in a separate struct with just the listenable
/// elements.
pub fn new(input_processor: &InputProcessor) -> Self {
let mut universe = Universe::new();
let hud_blocks = HudBlocks::new(&mut universe, 16);
let hud_space = HudLayout::default().new_space(&mut universe, &hud_blocks);
Expand All @@ -57,6 +64,8 @@ impl Vui {
aspect_ratio: 4. / 3., // arbitrary placeholder assumption

tooltip_age: None,

mouselook_mode: input_processor.mouselook_mode(),
}
}

Expand Down Expand Up @@ -104,6 +113,20 @@ impl Vui {
}

pub fn step(&mut self, timestep: Duration) -> UniverseStepInfo {
// Update crosshair block
// TODO: Do this with a dirty check
self.hud_space
.borrow_mut()
.set(
HudLayout::default().crosshair_position(),
if *self.mouselook_mode.get() {
&self.hud_blocks.icons[Icons::Crosshair]
} else {
&AIR
},
)
.unwrap(); // TODO: Handle internal errors better than panicking

if let Some(ref mut age) = self.tooltip_age {
*age += timestep;
if *age > Duration::from_secs(1) {
Expand Down Expand Up @@ -155,20 +178,6 @@ impl Vui {
text,
)
}

// TODO: handle errors in a local/transient way instead of propagating
// TODO: this should surely be a listener rather than an explicit setter??
pub(crate) fn set_crosshair_visible(&mut self, visible: bool) -> Result<(), SetCubeError> {
self.hud_space.borrow_mut().set(
HudLayout::default().crosshair_position(),
if visible {
&self.hud_blocks.icons[Icons::Crosshair]
} else {
&AIR
},
)?;
Ok(())
}
}

#[allow(unused)] // TODO: not yet used for real
Expand Down Expand Up @@ -201,9 +210,13 @@ pub(crate) fn draw_background(space: &mut Space) {
mod tests {
use super::*;

fn new_vui_for_test() -> Vui {
Vui::new(&InputProcessor::new())
}

#[test]
fn vui_smoke_test() {
let _ = Vui::new();
let _ = new_vui_for_test();
}

#[test]
Expand All @@ -214,7 +227,7 @@ mod tests {

#[test]
fn tooltip_timeout() {
let mut vui = Vui::new();
let mut vui = new_vui_for_test();
assert_eq!(vui.tooltip_age, None);
vui.set_tooltip_text("Hello world").unwrap();
assert_eq!(vui.tooltip_age, Some(Duration::from_secs(0)));
Expand Down

0 comments on commit 4c8c68d

Please sign in to comment.