Skip to content

Commit

Permalink
Merge pull request #427 from kas-gui/work
Browse files Browse the repository at this point in the history
Fix docs.rs builds for kas-widgets, kas-view, kas-resvg, kas-dylib
  • Loading branch information
dhardy authored Dec 12, 2023
2 parents 5d146ce + 266f461 commit 66c58eb
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 24 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.14.2] — 2023-12-12

- Add `kas-widgets::edit::InstantParseGuard` (#427)
- Fix doc builds for kas-widgets, kas-view, kas-resvg, kas-dylib (#427)

## [0.14.1] — 2023-12-12

The focus of this version is *input data*: widgets now have a `Data` associated type, passed by reference
Expand Down
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kas"
version = "0.14.1"
version = "0.14.2"
authors = ["Diggory Hardy <[email protected]>"]
edition = "2021"
license = "Apache-2.0"
Expand Down Expand Up @@ -119,10 +119,10 @@ unsafe_node = ["kas-core/unsafe_node"]

[dependencies]
kas-core = { version = "0.14.1", path = "crates/kas-core" }
kas-dylib = { version = "0.14.1", path = "crates/kas-dylib", optional = true }
kas-widgets = { version = "0.14.1", path = "crates/kas-widgets" }
kas-view = { version = "0.14.1", path = "crates/kas-view", optional = true }
kas-resvg = { version = "0.14.1", path = "crates/kas-resvg", optional = true }
kas-dylib = { version = "0.14.2", path = "crates/kas-dylib", optional = true }
kas-widgets = { version = "0.14.2", path = "crates/kas-widgets" }
kas-view = { version = "0.14.2", path = "crates/kas-view", optional = true }
kas-resvg = { version = "0.14.2", path = "crates/kas-resvg", optional = true }

[dependencies.kas-wgpu]
version = "0.14.1"
Expand Down
9 changes: 6 additions & 3 deletions crates/kas-dylib/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kas-dylib"
version = "0.14.1"
version = "0.14.2"
authors = ["Diggory Hardy <[email protected]>"]
edition = "2021"
license = "Apache-2.0"
Expand All @@ -11,6 +11,9 @@ keywords = ["gui"]
categories = ["gui"]
repository = "https://github.com/kas-gui/kas"

[package.metadata.docs.rs]
features = ["kas-core/winit", "kas-core/wayland"]

[lib]
crate-type = ["dylib"]

Expand All @@ -21,6 +24,6 @@ resvg = ["dep:kas-resvg"]

[dependencies]
kas-core = { version = "0.14.1", path = "../kas-core" }
kas-widgets = { version = "0.14.1", path = "../kas-widgets" }
kas-resvg = { version = "0.14.1", path = "../kas-resvg", optional = true }
kas-widgets = { version = "0.14.2", path = "../kas-widgets" }
kas-resvg = { version = "0.14.2", path = "../kas-resvg", optional = true }
kas-wgpu = { version = "0.14.1", path = "../kas-wgpu", default-features = false }
3 changes: 2 additions & 1 deletion crates/kas-resvg/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kas-resvg"
version = "0.14.1"
version = "0.14.2"
authors = ["Diggory Hardy <[email protected]>"]
edition = "2021"
license = "Apache-2.0"
Expand All @@ -13,6 +13,7 @@ repository = "https://github.com/kas-gui/kas"
exclude = ["/screenshots"]

[package.metadata.docs.rs]
features = ["svg", "kas/winit", "kas/wayland"]
all-features = true
rustdoc-args = ["--cfg", "doc_cfg"]
# To build locally:
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-view/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kas-view"
version = "0.14.1"
version = "0.14.2"
authors = ["Diggory Hardy <[email protected]>"]
edition = "2021"
license = "Apache-2.0"
Expand All @@ -13,7 +13,7 @@ repository = "https://github.com/kas-gui/kas"
exclude = ["/screenshots"]

[package.metadata.docs.rs]
features = []
features = ["kas/winit", "kas/wayland"]
rustdoc-args = ["--cfg", "doc_cfg"]
# To build locally:
# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --no-deps --open
Expand Down
4 changes: 2 additions & 2 deletions crates/kas-widgets/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "kas-widgets"
version = "0.14.1"
version = "0.14.2"
authors = ["Diggory Hardy <[email protected]>"]
edition = "2021"
license = "Apache-2.0"
Expand All @@ -13,7 +13,7 @@ repository = "https://github.com/kas-gui/kas"
exclude = ["/screenshots"]

[package.metadata.docs.rs]
features = ["min_spec"]
features = ["min_spec", "kas/winit", "kas/wayland"]
rustdoc-args = ["--cfg", "doc_cfg"]
# To build locally:
# RUSTDOCFLAGS="--cfg doc_cfg" cargo +nightly doc --no-deps --open
Expand Down
142 changes: 131 additions & 11 deletions crates/kas-widgets/src/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,11 @@ impl<A: 'static> EditGuard for DefaultGuard<A> {
}

impl_scope! {
/// An [`EditGuard`] impl for string input
/// An [`EditGuard`] for read-only strings
///
/// This may be used with read-only edit fields, essentially resulting in a
/// fancier version of [`Text`](crate::Text) or
/// [`ScrollText`](crate::ScrollText).
#[autoimpl(Debug ignore self.value_fn, self.on_afl)]
pub struct StringGuard<A> {
value_fn: Box<dyn Fn(&A) -> String>,
Expand Down Expand Up @@ -199,7 +203,11 @@ impl_scope! {
}

impl_scope! {
/// An [`EditGuard`] impl for simple parsable types (e.g. numbers)
/// An [`EditGuard`] for parsable types
///
/// This guard displays a value formatted from input data, updates the error
/// state according to parse success on each keystroke, and sends a message
/// on focus loss (where successful parsing occurred).
#[autoimpl(Debug ignore self.value_fn, self.on_afl)]
pub struct ParseGuard<A, T: Debug + Display + FromStr> {
parsed: Option<T>,
Expand Down Expand Up @@ -265,6 +273,68 @@ impl_scope! {
}
}

impl_scope! {
/// An as-you-type [`EditGuard`] for parsable types
///
/// This guard displays a value formatted from input data, updates the error
/// state according to parse success on each keystroke, and sends a message
/// immediately (where successful parsing occurred).
#[autoimpl(Debug ignore self.value_fn, self.on_afl)]
pub struct InstantParseGuard<A, T: Debug + Display + FromStr> {
value_fn: Box<dyn Fn(&A) -> T>,
on_afl: Box<dyn Fn(&mut EventCx, T)>,
}

impl Self {
/// Construct
///
/// On update, `value_fn` is used to extract a value from input data
/// which is then formatted as a string via [`Display`].
/// If, however, the input field has focus, the update is ignored.
///
/// On every edit, the guard attempts to parse the field's input as type
/// `T` via [`FromStr`]. On success, the result is converted to a
/// message via `on_afl` then emitted via [`EventCx::push`].
pub fn new<M: Debug + 'static>(
value_fn: impl Fn(&A) -> T + 'static,
on_afl: impl Fn(T) -> M + 'static,
) -> Self {
InstantParseGuard {
value_fn: Box::new(value_fn),
on_afl: Box::new(move |cx, value| cx.push(on_afl(value))),
}
}
}

impl EditGuard for Self {
type Data = A;

fn focus_lost(edit: &mut EditField<Self>, cx: &mut EventCx, data: &A) {
// Always reset data on focus loss
let value = (edit.guard.value_fn)(data);
let action = edit.set_string(format!("{}", value));
cx.action(edit, action);
}

fn edit(edit: &mut EditField<Self>, cx: &mut EventCx, _: &A) {
let result = edit.get_str().parse();
let action = edit.set_error_state(result.is_err());
cx.action(edit.id(), action);
if let Ok(value) = result {
(edit.guard.on_afl)(cx, value);
}
}

fn update(edit: &mut EditField<Self>, cx: &mut ConfigCx, data: &A) {
if !edit.has_edit_focus() {
let value = (edit.guard.value_fn)(data);
let action = edit.set_string(format!("{}", value));
cx.action(&edit, action);
}
}
}
}

impl_scope! {
/// A text-edit box
///
Expand Down Expand Up @@ -418,23 +488,48 @@ impl<A: 'static> EditBox<DefaultGuard<A>> {
}
}

/// Construct an `EditField` displaying some `String` value
///
/// The field is read-only. To make it read-write call [`Self::with_msg`]
/// or [`Self::with_editable`].
/// Construct a read-only `EditBox` displaying some `String` value
#[inline]
pub fn string(value_fn: impl Fn(&A) -> String + 'static) -> EditBox<StringGuard<A>> {
EditBox::new(StringGuard::new(value_fn)).with_editable(false)
}

/// Construct an `EditBox` for a parsable value (e.g. a number)
///
/// On update, `value_fn` is used to extract a value from input data
/// which is then formatted as a string via [`Display`].
/// If, however, the input field has focus, the update is ignored.
///
/// On every edit, the guard attempts to parse the field's input as type
/// `T` via [`FromStr`], caching the result and setting the error state.
///
/// On field activation and focus loss when a `T` value is cached (see
/// previous paragraph), `on_afl` is used to construct a message to be
/// emitted via [`EventCx::push`]. The cached value is then cleared to
/// avoid sending duplicate messages.
#[inline]
pub fn parser<T: Debug + Display + FromStr, M: Debug + 'static>(
value_fn: impl Fn(&A) -> T + 'static,
msg_fn: impl Fn(T) -> M + 'static,
) -> EditBox<ParseGuard<A, T>> {
EditBox::new(ParseGuard::new(value_fn, msg_fn))
}

/// Construct an `EditBox` for a parsable value (e.g. a number)
///
/// On update, `value_fn` is used to extract a value from input data
/// which is then formatted as a string via [`Display`].
/// If, however, the input field has focus, the update is ignored.
///
/// On every edit, the guard attempts to parse the field's input as type
/// `T` via [`FromStr`]. On success, the result is converted to a
/// message via `on_afl` then emitted via [`EventCx::push`].
pub fn instant_parser<T: Debug + Display + FromStr, M: Debug + 'static>(
value_fn: impl Fn(&A) -> T + 'static,
msg_fn: impl Fn(T) -> M + 'static,
) -> EditBox<InstantParseGuard<A, T>> {
EditBox::new(InstantParseGuard::new(value_fn, msg_fn))
}
}

impl<A: 'static> EditBox<StringGuard<A>> {
Expand Down Expand Up @@ -882,23 +977,48 @@ impl<A: 'static> EditField<DefaultGuard<A>> {
}
}

/// Construct an `EditField` displaying some `String` value
///
/// The field is read-only. To make it read-write call [`Self::with_msg`]
/// or [`Self::with_editable`].
/// Construct a read-only `EditField` displaying some `String` value
#[inline]
pub fn string(value_fn: impl Fn(&A) -> String + 'static) -> EditField<StringGuard<A>> {
EditField::new(StringGuard::new(value_fn)).with_editable(false)
}

/// Construct an `EditField` for a parsable value (e.g. a number)
///
/// On update, `value_fn` is used to extract a value from input data
/// which is then formatted as a string via [`Display`].
/// If, however, the input field has focus, the update is ignored.
///
/// On every edit, the guard attempts to parse the field's input as type
/// `T` via [`FromStr`], caching the result and setting the error state.
///
/// On field activation and focus loss when a `T` value is cached (see
/// previous paragraph), `on_afl` is used to construct a message to be
/// emitted via [`EventCx::push`]. The cached value is then cleared to
/// avoid sending duplicate messages.
#[inline]
pub fn parser<T: Debug + Display + FromStr, M: Debug + 'static>(
value_fn: impl Fn(&A) -> T + 'static,
msg_fn: impl Fn(T) -> M + 'static,
) -> EditField<ParseGuard<A, T>> {
EditField::new(ParseGuard::new(value_fn, msg_fn))
}

/// Construct an `EditField` for a parsable value (e.g. a number)
///
/// On update, `value_fn` is used to extract a value from input data
/// which is then formatted as a string via [`Display`].
/// If, however, the input field has focus, the update is ignored.
///
/// On every edit, the guard attempts to parse the field's input as type
/// `T` via [`FromStr`]. On success, the result is converted to a
/// message via `on_afl` then emitted via [`EventCx::push`].
pub fn instant_parser<T: Debug + Display + FromStr, M: Debug + 'static>(
value_fn: impl Fn(&A) -> T + 'static,
msg_fn: impl Fn(T) -> M + 'static,
) -> EditField<InstantParseGuard<A, T>> {
EditField::new(InstantParseGuard::new(value_fn, msg_fn))
}
}

impl<A: 'static> EditField<StringGuard<A>> {
Expand Down Expand Up @@ -939,7 +1059,7 @@ impl<G: EditGuard> EditField<G> {

/// Set the initial text (inline)
///
/// This method should only be used on a new `EditBox`.
/// This method should only be used on a new `EditField`.
#[inline]
#[must_use]
pub fn with_text(mut self, text: impl ToString) -> Self {
Expand Down

0 comments on commit 66c58eb

Please sign in to comment.