Skip to content

Commit

Permalink
ui: Add Settings type.
Browse files Browse the repository at this point in the history
This will be the platform-independent interface to persistent user
settings. For now, it just wraps the existing `GraphicsOptions`.

`ControlMessage::ModifyGraphicsOptions` is now `ModifySettings`,
which is straightforwardly one fewer TODO!
  • Loading branch information
kpreid committed Jan 6, 2025
1 parent 59b7ead commit eb09da6
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 74 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## Unreleased

### Added

- `all-is-cubes-ui` library:
- `apps::Settings` manages user-editable settings that eventually will be more than just the graphics options.
- `apps::SessionBuilder::settings()` links a possibly-shared `Settings` to the created session.

### Changed

- `all-is-cubes-ui` library:
- `apps::Session::settings()` replaces `graphics_options_mut()`.

## 0.9.0 (2025-01-01)

### Added
Expand Down
9 changes: 3 additions & 6 deletions all-is-cubes-desktop/src/bin/all-is-cubes/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,9 +111,7 @@ fn main() -> Result<(), anyhow::Error> {
.build(),
);
// TODO: this code should live in the lib
session
.graphics_options_mut()
.set(Arc::new(graphics_options));
session.settings().set_graphics_options(graphics_options);
universe_task.attach_to_session(&mut session);
let session_done_time = Instant::now();
log::debug!(
Expand Down Expand Up @@ -179,9 +177,8 @@ fn main() -> Result<(), anyhow::Error> {
.context("failed to create session")?;
if graphics_type == GraphicsType::WindowRt {
// TODO: improve on this kludge by just having a general cmdline graphics config
dsession.session.graphics_options_mut().update_mut(|o| {
Arc::make_mut(o).render_method =
all_is_cubes_render::camera::RenderMethod::Reference;
dsession.session.settings().mutate_graphics_options(|o| {
o.render_method = all_is_cubes_render::camera::RenderMethod::Reference;
});
}
Ok(dsession)
Expand Down
6 changes: 2 additions & 4 deletions all-is-cubes-desktop/src/record/rmain.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::fmt;
use std::sync::Arc;
use std::time::Duration;

use all_is_cubes::character::{self, Character};
Expand Down Expand Up @@ -37,9 +36,8 @@ where
// override it if they do want to record the UI.
dsession
.session
.graphics_options_mut()
.update_mut(|graphics_options| {
let graphics_options = Arc::make_mut(graphics_options);
.settings()
.mutate_graphics_options(|graphics_options| {
graphics_options.show_ui = false;
graphics_options.debug_info_text = false;
});
Expand Down
39 changes: 18 additions & 21 deletions all-is-cubes-ui/src/apps/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
reason = "false positive; TODO: remove after Rust 1.84 is released"
)]

use alloc::sync::Arc;
use alloc::vec::Vec;
use core::time::Duration;
use std::collections::{HashMap, HashSet};
Expand All @@ -15,11 +14,10 @@ use all_is_cubes::math::{zo32, FreeCoordinate, FreeVector};
use all_is_cubes::time::Tick;
use all_is_cubes::universe::{Handle, Universe};
use all_is_cubes_render::camera::{
FogOption, GraphicsOptions, LightingOption, NdcPoint2, NominalPixel, RenderMethod,
TransparencyOption, Viewport,
FogOption, LightingOption, NdcPoint2, NominalPixel, RenderMethod, TransparencyOption, Viewport,
};

use crate::apps::ControlMessage;
use crate::apps::{ControlMessage, Settings};

type MousePoint = Point2D<f64, NominalPixel>;

Expand Down Expand Up @@ -295,7 +293,7 @@ impl InputProcessor {
universe,
character: character_opt,
paused: paused_opt,
graphics_options,
settings,
control_channel,
ui,
} = targets;
Expand Down Expand Up @@ -349,10 +347,9 @@ impl InputProcessor {
}
}
Key::Character('i') => {
if let Some(cell) = graphics_options {
cell.update_mut(|options| {
Arc::make_mut(options).lighting_display = match options.lighting_display
{
if let Some(settings) = settings {
settings.mutate_graphics_options(|options| {
options.lighting_display = match options.lighting_display {
LightingOption::None => LightingOption::Flat,
LightingOption::Flat => LightingOption::Smooth,
LightingOption::Smooth => LightingOption::None,
Expand All @@ -371,9 +368,9 @@ impl InputProcessor {
}
}
Key::Character('o') => {
if let Some(cell) = graphics_options {
cell.update_mut(|options| {
Arc::make_mut(options).transparency = match options.transparency {
if let Some(settings) = settings {
settings.mutate_graphics_options(|options| {
options.transparency = match options.transparency {
TransparencyOption::Surface => TransparencyOption::Volumetric,
TransparencyOption::Volumetric => {
TransparencyOption::Threshold(zo32(0.5))
Expand All @@ -391,9 +388,9 @@ impl InputProcessor {
}
}
Key::Character('u') => {
if let Some(cell) = graphics_options {
cell.update_mut(|options| {
Arc::make_mut(options).fog = match options.fog {
if let Some(settings) = settings {
settings.mutate_graphics_options(|options| {
options.fog = match options.fog {
FogOption::None => FogOption::Abrupt,
FogOption::Abrupt => FogOption::Compromise,
FogOption::Compromise => FogOption::Physical,
Expand All @@ -404,9 +401,9 @@ impl InputProcessor {
}
}
Key::Character('y') => {
if let Some(cell) = graphics_options {
cell.update_mut(|options| {
Arc::make_mut(options).render_method = match options.render_method {
if let Some(settings) = settings {
settings.mutate_graphics_options(|options| {
options.render_method = match options.render_method {
RenderMethod::Mesh => RenderMethod::Reference,
RenderMethod::Reference => RenderMethod::Mesh,
_ => RenderMethod::Reference,
Expand Down Expand Up @@ -492,7 +489,7 @@ pub(crate) struct InputTargets<'a> {
pub universe: Option<&'a mut Universe>,
pub character: Option<&'a Handle<Character>>,
pub paused: Option<&'a listen::Cell<bool>>,
pub graphics_options: Option<&'a listen::Cell<Arc<GraphicsOptions>>>,
pub settings: Option<&'a Settings>,
// TODO: replace cells with control channel?
// TODO: make the control channel a type alias?
pub control_channel: Option<&'a flume::Sender<ControlMessage>>,
Expand Down Expand Up @@ -534,7 +531,7 @@ mod tests {
universe: Some(universe),
character: Some(character),
paused: None,
graphics_options: None,
settings: None,
control_channel: None,
ui: None,
},
Expand Down Expand Up @@ -583,7 +580,7 @@ mod tests {
&InputProcessor::new(),
listen::constant(None),
paused.as_source(),
listen::constant(Arc::new(GraphicsOptions::default())),
listen::constant(Default::default()),
cctx,
listen::constant(Viewport::ARBITRARY),
listen::constant(None),
Expand Down
3 changes: 3 additions & 0 deletions all-is-cubes-ui/src/apps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ pub use input::*;
mod session;
pub use session::*;

mod settings;
pub use settings::*;

mod time;
pub use time::*;
68 changes: 43 additions & 25 deletions all-is-cubes-ui/src/apps/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use all_is_cubes_render::camera::{
GraphicsOptions, Layers, StandardCameras, UiViewState, Viewport,
};

use crate::apps::{FpsCounter, FrameClock, InputProcessor, InputTargets};
use crate::apps::{FpsCounter, FrameClock, InputProcessor, InputTargets, Settings};
use crate::ui_content::notification::{self, Notification};
use crate::ui_content::Vui;

Expand Down Expand Up @@ -106,7 +106,7 @@ pub struct Session<I> {
/// might also be moved to a background task to allow the session stepping to occur independent
/// of the event loop or other owner of the `Session`.
struct Shuttle {
graphics_options: listen::Cell<Arc<GraphicsOptions>>,
settings: Settings,

game_universe: Universe,

Expand Down Expand Up @@ -159,7 +159,7 @@ impl<I: fmt::Debug> fmt::Debug for Session<I> {
return Ok(());
};
let Shuttle {
graphics_options,
settings,
game_universe,
game_universe_info,
game_character,
Expand All @@ -175,7 +175,7 @@ impl<I: fmt::Debug> fmt::Debug for Session<I> {
f.debug_struct("Session")
.field("frame_clock", frame_clock)
.field("input_processor", input_processor)
.field("graphics_options", graphics_options)
.field("settings", settings)
.field("game_universe", game_universe)
.field("game_universe_info", game_universe_info)
.field("game_character", game_character)
Expand Down Expand Up @@ -285,12 +285,14 @@ impl<I: time::Instant> Session<I> {

/// Allows reading, and observing changes to, the current graphics options.
pub fn graphics_options(&self) -> listen::DynSource<Arc<GraphicsOptions>> {
self.shuttle().graphics_options.as_source()
self.shuttle().settings.as_source()
}

/// Allows setting the current graphics options.
pub fn graphics_options_mut(&self) -> &listen::Cell<Arc<GraphicsOptions>> {
&self.shuttle().graphics_options
/// Allows changing the settings associated with this session.
///
/// Note that these settings may be shared with other sessions.
pub fn settings(&self) -> &Settings {
&self.shuttle().settings
}

/// Returns a [`StandardCameras`] which may be used in rendering a view of this session,
Expand Down Expand Up @@ -340,7 +342,7 @@ impl<I: time::Instant> Session<I> {
universe: Some(&mut shuttle.game_universe),
character: shuttle.game_character.get().as_ref(),
paused: Some(&self.paused),
graphics_options: Some(&shuttle.graphics_options),
settings: Some(&shuttle.settings),
control_channel: Some(&self.control_channel_sender),
ui: shuttle.ui.as_ref(),
},
Expand Down Expand Up @@ -493,11 +495,8 @@ impl<I: time::Instant> Session<I> {
ControlMessage::ToggleMouselook => {
self.input_processor.toggle_mouselook_mode();
}
ControlMessage::ModifyGraphicsOptions(f) => {
let shuttle = self.shuttle();
shuttle
.graphics_options
.set(f(shuttle.graphics_options.get()));
ControlMessage::ModifySettings(function) => {
function(&self.shuttle().settings);
}
},
Err(TryRecvError::Empty) => break,
Expand Down Expand Up @@ -632,8 +631,8 @@ impl<I: time::Instant> Session<I> {
let fopt = StatusText {
show: self
.shuttle()
.graphics_options
.get()
.settings
.get_graphics_options()
.debug_info_text_contents,
};

Expand Down Expand Up @@ -766,6 +765,8 @@ pub struct SessionBuilder<I> {
fullscreen_state: listen::DynSource<FullscreenState>,
set_fullscreen: FullscreenSetter,

settings: Option<Settings>,

quit: Option<QuitFn>,

_instant: PhantomData<I>,
Expand All @@ -777,6 +778,7 @@ impl<I> Default for SessionBuilder<I> {
viewport_for_ui: None,
fullscreen_state: listen::constant(None),
set_fullscreen: None,
settings: None,
quit: None,
_instant: PhantomData,
}
Expand All @@ -794,13 +796,16 @@ impl<I: time::Instant> SessionBuilder<I> {
viewport_for_ui,
fullscreen_state,
set_fullscreen,
settings,
quit: quit_fn,
_instant: _,
} = self;

let settings = settings.unwrap_or_else(|| Settings::new(Default::default()));

let game_universe = Universe::new();
let game_character = listen::CellWithLocal::new(None);
let input_processor = InputProcessor::new();
let graphics_options = listen::Cell::new(Arc::new(GraphicsOptions::default()));
let paused = listen::Cell::new(false);
let (control_send, control_recv) = flume::bounded(100);

Expand All @@ -816,7 +821,7 @@ impl<I: time::Instant> SessionBuilder<I> {
&input_processor,
game_character.as_source(),
paused.as_source(),
graphics_options.as_source(),
settings.as_source(),
control_send.clone(),
viewport,
fullscreen_state,
Expand All @@ -828,7 +833,7 @@ impl<I: time::Instant> SessionBuilder<I> {
None => None,
},

graphics_options,
settings,
game_character,
game_universe_info: listen::Cell::new(SessionUniverseInfo {
id: game_universe.universe_id(),
Expand Down Expand Up @@ -880,6 +885,15 @@ impl<I: time::Instant> SessionBuilder<I> {
self
}

/// Enable reading and writing user settings.
///
/// If this is not called, then the session will have all default settings,
/// and they will not be persisted.
pub fn settings(mut self, settings: Settings) -> Self {
self.settings = Some(settings);
self
}

/// Enable a “quit”/“exit” command in the session's user interface.
///
/// This does not cause the session to self-destruct; rather, the provided callback
Expand Down Expand Up @@ -915,8 +929,7 @@ pub(crate) enum ControlMessage {

ToggleMouselook,

/// TODO: this should be "modify user preferences", from which graphics options are derived.
ModifyGraphicsOptions(Box<dyn FnOnce(Arc<GraphicsOptions>) -> Arc<GraphicsOptions> + Send>),
ModifySettings(Box<dyn FnOnce(&Settings) + Send>),
}

impl fmt::Debug for ControlMessage {
Expand All @@ -929,9 +942,7 @@ impl fmt::Debug for ControlMessage {
Self::EnterDebug => write!(f, "EnterDebug"),
Self::TogglePause => write!(f, "TogglePause"),
Self::ToggleMouselook => write!(f, "ToggleMouselook"),
Self::ModifyGraphicsOptions(_func) => f
.debug_struct("ModifyGraphicsOptions")
.finish_non_exhaustive(),
Self::ModifySettings(_func) => f.debug_struct("ModifySettings").finish_non_exhaustive(),
}
}
}
Expand Down Expand Up @@ -1124,7 +1135,7 @@ impl MainTaskContext {
pub fn create_cameras(&self, viewport_source: listen::DynSource<Viewport>) -> StandardCameras {
self.with_ref(|shuttle| {
StandardCameras::new(
shuttle.graphics_options.as_source(),
shuttle.settings.as_source(),
viewport_source,
shuttle.game_character.as_source(),
shuttle.ui_view(),
Expand All @@ -1148,6 +1159,13 @@ impl MainTaskContext {
})
}

/// Allows reading or changing the settings of this session.
///
/// Note that these settings may be shared with other sessions.
pub fn settings(&self) -> Settings {
self.with_ref(|shuttle| shuttle.settings.clone())
}

/// Invoke the [`SessionBuilder::quit()`] callback as if the user clicked a quit button inside
/// our UI.
///
Expand Down
Loading

0 comments on commit eb09da6

Please sign in to comment.