Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config editor; revise model traits, Spinner widget, SizeMgr::dpem, ... #334

Merged
merged 27 commits into from
Jul 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
eae1785
Rename private module kas_widgets::adapter::label to with_label
dhardy Jul 11, 2022
083726d
examples/gallery: combine Label widgets
dhardy Jul 11, 2022
93afce4
Add kas_core::label::StrLabel
dhardy Jul 11, 2022
9632f19
Replace Vec2::max_abs_comp with max_comp (can be combined with Vec2::…
dhardy Jul 12, 2022
51b9b3b
Add const TkAction::EMPTY
dhardy Jul 13, 2022
1c259c5
Revise Spinner widget
dhardy Jul 12, 2022
7174275
Rename Spinner/SliderType traits to Spinner/SliderValue
dhardy Jul 12, 2022
0641869
Add impl of Driver<event::Config>
dhardy Jul 11, 2022
9bd3ad4
Use SharedRc<event::Config> instead of Rc<RefCell<Config>>
dhardy Jul 12, 2022
dfeadd7
Manager: update cached event::Config on update event
dhardy Jul 12, 2022
f2605c4
SharedRc is not for List/Matrix data
dhardy Jul 12, 2022
69f68da
kas::model: rename SingleData → SharedData and use as base of List/Ma…
dhardy Jul 12, 2022
9b2b358
Add kas::model::DataKey, SingleData
dhardy Jul 12, 2022
414a528
Add Data: SharedData type parameter to Driver
dhardy Jul 13, 2022
8b7b65c
examples/data-list-view: key type no longer needs to be part of data
dhardy Jul 13, 2022
9c1f274
Move SharedData::handle_message to Driver
dhardy Jul 13, 2022
5bc7eee
Rename kas_widgets::edit_field → edit and make a public module
dhardy Jul 13, 2022
bd39c9a
Slider, Spinner: add optional event handling closures
dhardy Jul 13, 2022
b605ba1
driver/config: Add handle_message
dhardy Jul 13, 2022
2ac95ba
driver/config: add Reset button
dhardy Jul 13, 2022
cf9a69a
Rename Driver::handle_message → on_message; add widget param
dhardy Jul 13, 2022
4f71a77
Gallery: add config tab
dhardy Jul 12, 2022
b3e61a6
SizeMgr: add dpem, min_scroll_size; remove pixels_from_*, line_height
dhardy Jul 13, 2022
8932eb8
Revise handling of scroll distance
dhardy Jul 13, 2022
b577da5
Add scroll_dist_em to driver/config; adjust default config values
dhardy Jul 12, 2022
f598ca6
Add event::Config::is_dirty field
dhardy Jul 13, 2022
7fb94fb
Clippy fix
dhardy Jul 14, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/kas-core/src/event/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ impl Glide {
let delta = Offset::conv_approx(d);
let rest = d - Vec2::conv(delta);

if v.max_abs_comp() >= 1.0 {
if v.abs().max_comp() >= 1.0 {
let mut v = *v * decay_mul.powf(dur);
v = v - v.abs().min(Vec2::splat(decay_sub * dur)) * v.sign();
*self = Glide::Glide(now, v, rest);
Expand Down Expand Up @@ -251,7 +251,7 @@ impl ScrollComponent {
_ => return (false, Response::Unused),
};
let delta = match delta {
LineDelta(x, y) => mgr.config().scroll_distance((-x, y), None),
LineDelta(x, y) => mgr.config().scroll_distance((x, y)),
PixelDelta(d) => d,
};
self.offset - delta
Expand All @@ -266,7 +266,7 @@ impl ScrollComponent {
}
Event::Scroll(delta) => {
let delta = match delta {
LineDelta(x, y) => mgr.config().scroll_distance((-x, y), None),
LineDelta(x, y) => mgr.config().scroll_distance((x, y)),
PixelDelta(d) => d,
};
moved = self.scroll_by_delta(mgr, delta);
Expand Down
91 changes: 43 additions & 48 deletions crates/kas-core/src/event/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ pub use shortcuts::Shortcuts;
use super::ModifiersState;
use crate::cast::{Cast, CastFloat};
use crate::geom::Offset;
use crate::model::SharedRc;
#[cfg(feature = "config")]
use serde::{Deserialize, Serialize};
use std::cell::RefCell;
use std::rc::Rc;
use std::time::Duration;

/// Event handling configuration
Expand Down Expand Up @@ -43,9 +42,6 @@ pub struct Config {
#[cfg_attr(feature = "config", serde(default = "defaults::touch_select_delay_ms"))]
pub touch_select_delay_ms: u32,

#[cfg_attr(feature = "config", serde(default = "defaults::scroll_lines"))]
pub scroll_lines: f32,

#[cfg_attr(
feature = "config",
serde(default = "defaults::scroll_flick_timeout_ms")
Expand All @@ -58,6 +54,9 @@ pub struct Config {
#[cfg_attr(feature = "config", serde(default = "defaults::scroll_flick_sub"))]
pub scroll_flick_sub: f32,

#[cfg_attr(feature = "config", serde(default = "defaults::scroll_dist_em"))]
pub scroll_dist_em: f32,

#[cfg_attr(feature = "config", serde(default = "defaults::pan_dist_thresh"))]
pub pan_dist_thresh: f32,

Expand All @@ -73,62 +72,75 @@ pub struct Config {

#[cfg_attr(feature = "config", serde(default = "Shortcuts::platform_defaults"))]
pub shortcuts: Shortcuts,

#[cfg_attr(feature = "config", serde(skip))]
pub is_dirty: bool,
}

impl Default for Config {
fn default() -> Self {
Config {
menu_delay_ms: defaults::menu_delay_ms(),
touch_select_delay_ms: defaults::touch_select_delay_ms(),
scroll_lines: defaults::scroll_lines(),
scroll_flick_timeout_ms: defaults::scroll_flick_timeout_ms(),
scroll_flick_mul: defaults::scroll_flick_mul(),
scroll_flick_sub: defaults::scroll_flick_sub(),
scroll_dist_em: defaults::scroll_dist_em(),
pan_dist_thresh: defaults::pan_dist_thresh(),
mouse_pan: defaults::mouse_pan(),
mouse_text_pan: defaults::mouse_text_pan(),
mouse_nav_focus: defaults::mouse_nav_focus(),
touch_nav_focus: defaults::touch_nav_focus(),
shortcuts: Shortcuts::platform_defaults(),
is_dirty: false,
}
}
}

impl Config {
/// Has the config ever been updated?
#[inline]
pub fn is_dirty(&self) -> bool {
self.is_dirty
}
}

/// Wrapper around [`Config`] to handle window-specific scaling
#[derive(Clone, Debug)]
pub struct WindowConfig {
config: Rc<RefCell<Config>>,
scroll_dist: f32,
pub(crate) config: SharedRc<Config>,
scroll_flick_sub: f32,
scroll_dist: f32,
pan_dist_thresh: f32,
}

impl WindowConfig {
/// Construct
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub fn new(config: Rc<RefCell<Config>>, scale_factor: f32) -> Self {
pub fn new(config: SharedRc<Config>, scale_factor: f32, dpem: f32) -> Self {
let mut w = WindowConfig {
config,
scroll_dist: f32::NAN,
scroll_flick_sub: f32::NAN,
scroll_dist: f32::NAN,
pan_dist_thresh: f32::NAN,
};
w.set_scale_factor(scale_factor);
w.update(scale_factor, dpem);
w
}

/// Set scale factor
/// Update
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub fn set_scale_factor(&mut self, scale_factor: f32) {
pub fn update(&mut self, scale_factor: f32, dpem: f32) {
let base = self.config.borrow();
const LINE_HEIGHT: f32 = 19.0; // TODO: maybe we shouldn't assume this?
self.scroll_dist = base.scroll_lines * LINE_HEIGHT;
self.scroll_flick_sub = base.scroll_flick_sub * scale_factor;
self.scroll_dist = base.scroll_dist_em * dpem;
self.pan_dist_thresh = base.pan_dist_thresh * scale_factor;
}
}

impl WindowConfig {
/// Delay before opening/closing menus on mouse hover
#[inline]
pub fn menu_delay(&self) -> Duration {
Expand All @@ -141,23 +153,6 @@ impl WindowConfig {
Duration::from_millis(self.config.borrow().touch_select_delay_ms.cast())
}

/// Get distance in pixels to scroll due to mouse wheel
///
/// Calculates scroll distance from `(horiz, vert)` lines.
///
/// If `line_height` is provided, scroll distance is based on this value,
/// otherwise it is based on an arbitrary line height.
pub fn scroll_distance(&self, lines: (f32, f32), line_height: Option<f32>) -> Offset {
let dist = match line_height {
Some(height) => height * self.config.borrow().scroll_lines,
None => self.scroll_dist,
};
Offset(
(dist * lines.0).cast_nearest(),
(dist * lines.1).cast_nearest(),
)
}

/// Controls activation of glide/momentum scrolling
///
/// This is the maximum time between the last press-movement and final
Expand All @@ -183,6 +178,15 @@ impl WindowConfig {
(self.config.borrow().scroll_flick_mul, self.scroll_flick_sub)
}

/// Get distance in pixels to scroll due to mouse wheel
///
/// Calculates scroll distance from `(horiz, vert)` lines.
pub fn scroll_distance(&self, lines: (f32, f32)) -> Offset {
let x = (self.scroll_dist * -lines.0).cast_nearest();
let y = (self.scroll_dist * lines.1).cast_nearest();
Offset(x, y)
}

/// Drag distance threshold before panning (scrolling) starts
///
/// When the distance moved is greater than this threshold, panning should
Expand Down Expand Up @@ -226,15 +230,6 @@ impl WindowConfig {
}
}

/// Other functions
impl Config {
/// Has the config ever been updated?
#[inline]
pub fn is_dirty(&self) -> bool {
false // current code never updates config
}
}

/// When mouse-panning is enabled (click+drag to scroll)
///
/// For *text* objects, this may conflict with text selection, hence it is
Expand Down Expand Up @@ -279,20 +274,20 @@ mod defaults {
pub fn touch_select_delay_ms() -> u32 {
1000
}
pub fn scroll_lines() -> f32 {
3.0
}
pub fn scroll_flick_timeout_ms() -> u32 {
25
50
}
pub fn scroll_flick_mul() -> f32 {
0.5
0.625
}
pub fn scroll_flick_sub() -> f32 {
100.0
200.0
}
pub fn scroll_dist_em() -> f32 {
4.5
}
pub fn pan_dist_thresh() -> f32 {
2.1
5.0
}
pub fn mouse_pan() -> MousePan {
MousePan::Always
Expand Down
6 changes: 4 additions & 2 deletions crates/kas-core/src/event/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ use linear_map::LinearMap;
use log::{trace, warn};
use smallvec::SmallVec;
use std::any::{type_name, Any};
use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap, VecDeque};
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::time::Instant;
use std::u16;

Expand Down Expand Up @@ -385,6 +383,10 @@ impl Message {
fn downcast<T: 'static>(self) -> Result<Box<T>, Box<dyn Any>> {
self.any.downcast::<T>()
}

fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.any.downcast_ref::<T>()
}
}

/// Manager of event-handling and toolkit actions
Expand Down
8 changes: 6 additions & 2 deletions crates/kas-core/src/event/manager/mgr_pub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ impl EventState {
/// Returns true when `dist` is large enough to switch to pan mode.
#[inline]
pub fn config_test_pan_thresh(&self, dist: Offset) -> bool {
let thresh = self.config.pan_dist_thresh();
Vec2::conv(dist).sum_square() >= thresh * thresh
Vec2::conv(dist).abs().max_comp() >= self.config.pan_dist_thresh()
}

/// Set/unset a widget as disabled
Expand Down Expand Up @@ -509,6 +508,11 @@ impl<'a> EventMgr<'a> {
}
}

/// Try observing the last message on the stack without popping
pub fn try_observe_msg<M: Debug + 'static>(&self) -> Option<&M> {
self.messages.last().and_then(|m| m.downcast_ref::<M>())
}

/// Set a scroll action
///
/// When setting [`Scroll::Rect`], use the widgets own coordinate space.
Expand Down
14 changes: 10 additions & 4 deletions crates/kas-core/src/event/manager/mgr_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::time::{Duration, Instant};
use super::*;
use crate::cast::traits::*;
use crate::geom::{Coord, DVec2};
use crate::model::SharedRc;
use crate::{ShellWindow, TkAction, Widget, WidgetId};

// TODO: this should be configurable or derived from the system
Expand All @@ -25,9 +26,9 @@ const FAKE_MOUSE_BUTTON: MouseButton = MouseButton::Other(0);
impl EventState {
/// Construct an event manager per-window data struct
#[inline]
pub fn new(config: Rc<RefCell<Config>>, scale_factor: f32) -> Self {
pub fn new(config: SharedRc<Config>, scale_factor: f32, dpem: f32) -> Self {
EventState {
config: WindowConfig::new(config, scale_factor),
config: WindowConfig::new(config, scale_factor, dpem),
disabled: vec![],
window_has_focus: false,
modifiers: ModifiersState::empty(),
Expand Down Expand Up @@ -55,8 +56,8 @@ impl EventState {
}

/// Update scale factor
pub fn set_scale_factor(&mut self, scale_factor: f32) {
self.config.set_scale_factor(scale_factor);
pub fn set_scale_factor(&mut self, scale_factor: f32, dpem: f32) {
self.config.update(scale_factor, dpem);
}

/// Configure event manager for a widget tree.
Expand Down Expand Up @@ -251,6 +252,11 @@ impl<'a> EventMgr<'a> {

/// Update widgets with an [`UpdateId`]
pub fn update_widgets(&mut self, widget: &mut dyn Widget, id: UpdateId, payload: u64) {
if id == self.state.config.config.id() {
let (sf, dpem) = self.size_mgr(|size| (size.scale_factor(), size.dpem()));
self.state.config.update(sf, dpem);
}

let start = Instant::now();
let count = self.send_all(widget, Event::Update { id, payload });
debug!(
Expand Down
6 changes: 3 additions & 3 deletions crates/kas-core/src/geom/vector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,10 @@ macro_rules! impl_vec2 {
self.0.min(self.1)
}

/// Take the maximum of absolute values of components
/// Take the maximum component
#[inline]
pub fn max_abs_comp(self) -> $f {
self.0.abs().max(self.1.abs())
pub fn max_comp(self) -> $f {
self.0.max(self.1)
}

/// Return the minimum, componentwise
Expand Down
69 changes: 69 additions & 0 deletions crates/kas-core/src/label.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
// https://www.apache.org/licenses/LICENSE-2.0

//! Simplified text label
//!
//! This module supports text strings in macros.
//! Direct usage of this module from user code is not supported.

use crate::class::HasStr;
use crate::event::ConfigMgr;
use crate::geom::Rect;
use crate::layout::{Align, AlignHints, AxisInfo, SizeRules};
use crate::text::{Text, TextApi};
use crate::theme::{DrawMgr, SizeMgr, TextClass};
use crate::{Layout, WidgetCore};
use kas_macros::impl_scope;

impl_scope! {
/// A simple text label
///
/// Vertical alignment defaults to centred, horizontal
/// alignment depends on the script direction if not specified.
/// Line-wrapping is enabled.
#[derive(Clone, Debug, Default)]
#[widget]
pub struct StrLabel {
core: widget_core!(),
label: Text<&'static str>,
}

impl Self {
/// Construct from `label`
#[inline]
pub fn new(label: &'static str) -> Self {
StrLabel {
core: Default::default(),
label: Text::new(label),
}
}

/// Text class
pub const CLASS: TextClass = TextClass::Label(true);
}

impl Layout for Self {
#[inline]
fn size_rules(&mut self, size_mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
size_mgr.text_bound(&mut self.label, Self::CLASS, axis)
}

fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect, align: AlignHints) {
self.core.rect = rect;
let align = align.unwrap_or(Align::Default, Align::Center);
mgr.text_set_size(&mut self.label, Self::CLASS, rect.size, align);
}

fn draw(&mut self, mut draw: DrawMgr) {
draw.text(self.rect().pos, &self.label, Self::CLASS);
}
}

impl HasStr for Self {
fn get_str(&self) -> &str {
self.label.as_str()
}
}
}
Loading