Skip to content

Commit

Permalink
Merge pull request #449 from kas-gui/work1
Browse files Browse the repository at this point in the history
Revise configuration support
  • Loading branch information
dhardy authored May 1, 2024
2 parents a5bb5b3 + 546d08e commit a86c39c
Show file tree
Hide file tree
Showing 56 changed files with 1,404 additions and 1,425 deletions.
21 changes: 18 additions & 3 deletions crates/kas-core/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ bitflags! {
/// while others don't reqiure a context but do require that some *action*
/// is performed afterwards. This enum is used to convey that action.
///
/// An `Action` should be passed to a context: `cx.action(self.id(), action)`
/// (assuming `self` is a widget).
/// An `Action` produced at run-time should be passed to a context:
/// `cx.action(self.id(), action)` (assuming `self` is a widget).
/// An `Action` produced before starting the GUI may be discarded, for
/// example: `let _ = app.config_mut().font.set_size(24.0);`.
///
/// Two `Action` values may be combined via bit-or (`a | b`).
#[must_use]
Expand Down Expand Up @@ -41,18 +43,31 @@ bitflags! {
const SET_RECT = 1 << 8;
/// Resize all widgets in the window
const RESIZE = 1 << 9;
/// Update theme memory
/// Update [`Dimensions`](crate::theme::dimensions::Dimensions) instances
/// and theme configuration.
///
/// Implies [`Action::RESIZE`].
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
const THEME_UPDATE = 1 << 10;
/// Reload per-window cache of event configuration
///
/// Implies [`Action::UPDATE`].
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
const EVENT_CONFIG = 1 << 11;
/// Switch themes, replacing theme-window instances
///
/// Implies [`Action::RESIZE`].
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
const THEME_SWITCH = 1 << 12;
/// Reconfigure all widgets of the window
///
/// *Configuring* widgets assigns [`Id`](crate::Id) identifiers and calls
/// [`Events::configure`](crate::Events::configure).
///
/// Implies [`Action::UPDATE`] since widgets are updated on configure.
const RECONFIGURE = 1 << 16;
/// Update all widgets
///
Expand Down
37 changes: 23 additions & 14 deletions crates/kas-core/src/app/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
//! [`Application`] and supporting elements
use super::{AppData, AppGraphicsBuilder, AppState, Platform, ProxyAction, Result};
use crate::config::Options;
use crate::config::{Config, Options};
use crate::draw::{DrawShared, DrawSharedImpl};
use crate::event;
use crate::theme::{self, Theme, ThemeConfig};
use crate::theme::{self, Theme};
use crate::util::warn_about_error;
use crate::{impl_scope, Window, WindowId};
use std::cell::RefCell;
use std::cell::{Ref, RefCell, RefMut};
use std::rc::Rc;
use winit::event_loop::{EventLoop, EventLoopBuilder, EventLoopProxy};

Expand All @@ -27,7 +26,7 @@ impl_scope! {
graphical: G,
theme: T,
options: Option<Options>,
config: Option<Rc<RefCell<event::Config>>>,
config: Option<Rc<RefCell<Config>>>,
}

impl Self {
Expand All @@ -54,29 +53,26 @@ impl_scope! {

/// Use the specified event `config`
///
/// This is a wrapper around [`Self::with_event_config_rc`].
/// This is a wrapper around [`Self::with_config_rc`].
///
/// If omitted, config is provided by [`Options::read_config`].
#[inline]
pub fn with_event_config(self, config: event::Config) -> Self {
self.with_event_config_rc(Rc::new(RefCell::new(config)))
pub fn with_config(self, config: Config) -> Self {
self.with_config_rc(Rc::new(RefCell::new(config)))
}

/// Use the specified event `config`
///
/// If omitted, config is provided by [`Options::read_config`].
#[inline]
pub fn with_event_config_rc(mut self, config: Rc<RefCell<event::Config>>) -> Self {
pub fn with_config_rc(mut self, config: Rc<RefCell<Config>>) -> Self {
self.config = Some(config);
self
}

/// Build with `data`
pub fn build<Data: AppData>(self, data: Data) -> Result<Application<Data, G, T>> {
let mut theme = self.theme;

let options = self.options.unwrap_or_else(Options::from_env);
options.init_theme_config(&mut theme)?;

let config = self.config.unwrap_or_else(|| match options.read_config() {
Ok(config) => Rc::new(RefCell::new(config)),
Expand All @@ -85,14 +81,15 @@ impl_scope! {
Default::default()
}
});
config.borrow_mut().init();

let el = EventLoopBuilder::with_user_event().build()?;

let mut draw_shared = self.graphical.build()?;
draw_shared.set_raster_config(theme.config().raster());
draw_shared.set_raster_config(config.borrow().font.raster());

let pw = PlatformWrapper(&el);
let state = AppState::new(data, pw, draw_shared, theme, options, config)?;
let state = AppState::new(data, pw, draw_shared, self.theme, options, config)?;

Ok(Application {
el,
Expand Down Expand Up @@ -166,6 +163,18 @@ where
&mut self.state.shared.draw
}

/// Access config
#[inline]
pub fn config(&self) -> Ref<Config> {
self.state.shared.config.borrow()
}

/// Access config mutably
#[inline]
pub fn config_mut(&mut self) -> RefMut<Config> {
self.state.shared.config.borrow_mut()
}

/// Access the theme by ref
#[inline]
pub fn theme(&self) -> &T {
Expand Down
6 changes: 3 additions & 3 deletions crates/kas-core/src/app/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
use super::{AppData, AppGraphicsBuilder, AppState, Pending};
use super::{ProxyAction, Window};
use kas::theme::Theme;
use kas::{Action, WindowId};
use crate::theme::Theme;
use crate::{Action, WindowId};
use std::collections::HashMap;
use std::time::Instant;
use winit::event::{Event, StartCause};
Expand Down Expand Up @@ -215,7 +215,7 @@ where
elwt.set_control_flow(ControlFlow::Poll);
} else {
for (_, window) in self.windows.iter_mut() {
window.handle_action(&self.state, action);
window.handle_action(&mut self.state, action);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/kas-core/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ mod test {
todo!()
}

fn set_raster_config(&mut self, _: &crate::theme::RasterConfig) {
fn set_raster_config(&mut self, _: &crate::config::RasterConfig) {
todo!()
}

Expand Down
36 changes: 10 additions & 26 deletions crates/kas-core/src/app/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
//! Shared state
use super::{AppData, AppGraphicsBuilder, Error, Pending, Platform};
use kas::config::Options;
use kas::draw::DrawShared;
use kas::theme::{Theme, ThemeControl};
use kas::util::warn_about_error;
use kas::{draw, messages::MessageStack, Action, WindowId};
use crate::config::{Config, Options};
use crate::draw::DrawShared;
use crate::theme::Theme;
use crate::util::warn_about_error;
use crate::{draw, messages::MessageStack, Action, WindowId};
use std::any::TypeId;
use std::cell::RefCell;
use std::collections::VecDeque;
Expand All @@ -23,7 +23,7 @@ use std::task::Waker;
/// Application state used by [`AppShared`]
pub(crate) struct AppSharedState<Data: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> {
pub(super) platform: Platform,
pub(super) config: Rc<RefCell<kas::event::Config>>,
pub(super) config: Rc<RefCell<Config>>,
#[cfg(feature = "clipboard")]
clipboard: Option<Clipboard>,
pub(super) draw: draw::SharedState<G::Shared>,
Expand Down Expand Up @@ -52,11 +52,11 @@ where
draw_shared: G::Shared,
mut theme: T,
options: Options,
config: Rc<RefCell<kas::event::Config>>,
config: Rc<RefCell<Config>>,
) -> Result<Self, Error> {
let platform = pw.platform();
let mut draw = kas::draw::SharedState::new(draw_shared);
theme.init(&mut draw);
let draw = kas::draw::SharedState::new(draw_shared);
theme.init(&config);

#[cfg(feature = "clipboard")]
let clipboard = match Clipboard::new() {
Expand Down Expand Up @@ -98,10 +98,7 @@ where
}

pub(crate) fn on_exit(&self) {
match self
.options
.write_config(&self.shared.config.borrow(), &self.shared.theme)
{
match self.options.write_config(&self.shared.config.borrow()) {
Ok(()) => (),
Err(error) => warn_about_error("Failed to save config", &error),
}
Expand Down Expand Up @@ -192,14 +189,6 @@ pub(crate) trait AppShared {
/// clipboard support.
fn set_primary(&mut self, content: String);

/// Adjust the theme
///
/// Note: theme adjustments apply to all windows, as does the [`Action`]
/// returned from the closure.
//
// TODO(opt): pass f by value, not boxed
fn adjust_theme<'s>(&'s mut self, f: Box<dyn FnOnce(&mut dyn ThemeControl) -> Action + 's>);

/// Access the [`DrawShared`] object
fn draw_shared(&mut self) -> &mut dyn DrawShared;

Expand Down Expand Up @@ -303,11 +292,6 @@ impl<Data: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> AppShared
}
}

fn adjust_theme<'s>(&'s mut self, f: Box<dyn FnOnce(&mut dyn ThemeControl) -> Action + 's>) {
let action = f(&mut self.theme);
self.pending.push_back(Pending::Action(action));
}

fn draw_shared(&mut self) -> &mut dyn DrawShared {
&mut self.draw
}
Expand Down
60 changes: 34 additions & 26 deletions crates/kas-core/src/app/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
use super::common::WindowSurface;
use super::shared::{AppSharedState, AppState};
use super::{AppData, AppGraphicsBuilder, ProxyAction};
use kas::cast::{Cast, Conv};
use kas::draw::{color::Rgba, AnimationState, DrawSharedImpl};
use kas::event::{config::WindowConfig, ConfigCx, CursorIcon, EventState};
use kas::geom::{Coord, Rect, Size};
use kas::layout::SolveCache;
use kas::theme::{DrawCx, SizeCx, ThemeSize};
use kas::theme::{Theme, Window as _};
use kas::{autoimpl, messages::MessageStack, Action, Id, Layout, LayoutExt, Widget, WindowId};
use crate::cast::{Cast, Conv};
use crate::config::WindowConfig;
use crate::draw::{color::Rgba, AnimationState, DrawSharedImpl};
use crate::event::{ConfigCx, CursorIcon, EventState};
use crate::geom::{Coord, Rect, Size};
use crate::layout::SolveCache;
use crate::theme::{DrawCx, SizeCx, Theme, ThemeSize, Window as _};
use crate::{autoimpl, messages::MessageStack, Action, Id, Layout, LayoutExt, Widget, WindowId};
use std::mem::take;
use std::sync::Arc;
use std::time::{Duration, Instant};
Expand Down Expand Up @@ -79,9 +79,11 @@ impl<A: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> Window<A, G, T> {

// We cannot reliably determine the scale factor before window creation.
// A factor of 1.0 lets us estimate the size requirements (logical).
let mut theme_window = state.shared.theme.new_window(1.0);
let dpem = theme_window.size().dpem();
self.ev_state.update_config(1.0, dpem);
self.ev_state.update_config(1.0);

let config = self.ev_state.config();
let mut theme_window = state.shared.theme.new_window(config);

self.ev_state.full_configure(
theme_window.size(),
self.window_id,
Expand Down Expand Up @@ -122,10 +124,11 @@ impl<A: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> Window<A, G, T> {
// Now that we have a scale factor, we may need to resize:
let scale_factor = window.scale_factor();
if scale_factor != 1.0 {
let sf32 = scale_factor as f32;
state.shared.theme.update_window(&mut theme_window, sf32);
let dpem = theme_window.size().dpem();
self.ev_state.update_config(sf32, dpem);
self.ev_state.update_config(scale_factor as f32);

let config = self.ev_state.config();
state.shared.theme.update_window(&mut theme_window, config);

let node = self.widget.as_node(&state.data);
let sizer = SizeCx::new(theme_window.size());
solve_cache = SolveCache::find_constraints(node, sizer);
Expand Down Expand Up @@ -226,13 +229,13 @@ impl<A: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> Window<A, G, T> {
}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
// Note: API allows us to set new window size here.
let scale_factor = scale_factor as f32;
self.ev_state.update_config(scale_factor as f32);

let config = self.ev_state.config();
state
.shared
.theme
.update_window(&mut window.theme_window, scale_factor);
let dpem = window.theme_window.size().dpem();
self.ev_state.update_config(scale_factor, dpem);
.update_window(&mut window.theme_window, config);

// NOTE: we could try resizing here in case the window is too
// small due to non-linear scaling, but it appears unnecessary.
Expand Down Expand Up @@ -298,12 +301,10 @@ impl<A: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> Window<A, G, T> {
}

/// Handle an action (excludes handling of CLOSE and EXIT)
pub(super) fn handle_action(&mut self, state: &AppState<A, G, T>, mut action: Action) {
pub(super) fn handle_action(&mut self, state: &mut AppState<A, G, T>, mut action: Action) {
if action.contains(Action::EVENT_CONFIG) {
if let Some(ref mut window) = self.window {
let scale_factor = window.scale_factor() as f32;
let dpem = window.theme_window.size().dpem();
self.ev_state.update_config(scale_factor, dpem);
self.ev_state.update_config(window.scale_factor() as f32);
action |= Action::UPDATE;
}
}
Expand All @@ -312,14 +313,21 @@ impl<A: AppData, G: AppGraphicsBuilder, T: Theme<G::Shared>> Window<A, G, T> {
} else if action.contains(Action::UPDATE) {
self.update(state);
}
if action.contains(Action::THEME_UPDATE) {
if action.contains(Action::THEME_SWITCH) {
if let Some(ref mut window) = self.window {
let config = self.ev_state.config();
window.theme_window = state.shared.theme.new_window(config);
}
action |= Action::RESIZE;
} else if action.contains(Action::THEME_UPDATE) {
if let Some(ref mut window) = self.window {
let scale_factor = window.scale_factor() as f32;
let config = self.ev_state.config();
state
.shared
.theme
.update_window(&mut window.theme_window, scale_factor);
.update_window(&mut window.theme_window, config);
}
action |= Action::RESIZE;
}
if action.contains(Action::RESIZE) {
if let Some(ref mut window) = self.window {
Expand Down
Loading

0 comments on commit a86c39c

Please sign in to comment.