Skip to content

Commit

Permalink
Label with animated variable font parameters (#507)
Browse files Browse the repository at this point in the history
This adds a new `VariableLabel` widget, which animates its weight to a
target value in a linear fashion (over a fixed time period). Also adds
support for this in Xilem, and a new `variable_clock` example. This
example also runs on Android.


[Screencast_20240812_171138.webm](https://github.com/user-attachments/assets/5df623f9-f4ca-4b55-b6a9-2047d2581b56)

Current status: The code in Xilem and Masonry library crates is final.
I'm planning on significantly updating the actual example.

Outstanding issues:

- [X] Hacks in support for "Roboto Flex", by always loading it from the
local file - resolved
- [X] It's not clear what subset of Roboto Flex we should use - still
open to bikeshedding
- [ ] The variable font animation support is not really as generic as it
should be. This starts to drift quite close to a styling question,
however.
- [ ] The only supported variable axis is `wgth`
  • Loading branch information
DJMcNab authored Aug 16, 2024
1 parent 052ac39 commit 3fd3903
Show file tree
Hide file tree
Showing 24 changed files with 1,112 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ env:
RUST_MIN_VER_WASM_PKGS: "-p xilem_core"

# Only some of our examples support Android (primarily due to extra required boilerplate).
ANDROID_TARGETS: -p xilem --example mason_android --example calc_android --example stopwatch_android
ANDROID_TARGETS: -p xilem --example mason_android --example calc_android --example stopwatch_android --example variable_clock_android

# We do not run the masonry snapshot tests, because those currently require a specific font stack
# See https://github.com/linebender/xilem/pull/233
Expand Down
1 change: 1 addition & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ extend-ignore-re = [

[default.extend-identifiers]
FillStrat = "FillStrat" # short for strategy
wdth = "wdth" # Variable font parameter

# Case insensitive
[default.extend-words]
Expand Down
13 changes: 12 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,4 @@ bitflags = "2.6.0"
accesskit = "0.16.0"
accesskit_winit = "0.22.0"
nv-flip = "0.1.2"
time = "0.3.36"
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ cargo update -p package_name --precise 0.1.1
Licensed under the Apache License, Version 2.0
([LICENSE](LICENSE) or <http://www.apache.org/licenses/LICENSE-2.0>)

The font file (`RobotoFlex-Subset.ttf`) in `xilem/resources/fonts/roboto_flex/` is licensed solely as documented in that folder,
(and is not licensed under the Apache License, Version 2.0).

## Contribution

Contributions are welcome by pull request. The [Rust code of conduct] applies.
Expand Down
2 changes: 1 addition & 1 deletion masonry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ xi-unicode = "0.3.0"
tracing-subscriber = { version = "0.3.18", features = ["env-filter", "time"] }
accesskit.workspace = true
accesskit_winit.workspace = true
time = { version = "0.3.36", features = ["macros", "formatting"] }
time = { workspace = true, features = ["macros", "formatting"] }
cursor-icon = "1.1.0"
dpi.workspace = true
nv-flip.workspace = true
Expand Down
7 changes: 7 additions & 0 deletions masonry/src/app_driver.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2024 the Xilem Authors
// SPDX-License-Identifier: Apache-2.0

use crate::event_loop_runner::MasonryState;
use crate::widget::WidgetMut;
use crate::{Action, Widget, WidgetId};

Expand All @@ -18,6 +19,12 @@ pub struct DriverCtx<'a> {

pub trait AppDriver {
fn on_action(&mut self, ctx: &mut DriverCtx<'_>, widget_id: WidgetId, action: Action);

#[allow(unused_variables)]
/// A hook which will be executed when the application starts, to allow initial configuration of the `MasonryState`.
///
/// Use cases include loading fonts.
fn on_start(&mut self, state: &mut MasonryState) {}
}

impl<'a> DriverCtx<'a> {
Expand Down
13 changes: 8 additions & 5 deletions masonry/src/event_loop_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,20 @@ pub fn run_with(
app_driver: impl AppDriver + 'static,
background_color: Color,
) -> Result<(), EventLoopError> {
let mut main_state = MainState {
masonry_state: MasonryState::new(window, &event_loop, root_widget, background_color),
app_driver: Box::new(app_driver),
};

// If there is no default tracing subscriber, we set our own. If one has
// already been set, we get an error which we swallow.
// By now, we're about to take control of the event loop. The user is unlikely
// to try to set their own subscriber once the event loop has started.
let _ = crate::tracing_backend::try_init_tracing();

let mut main_state = MainState {
masonry_state: MasonryState::new(window, &event_loop, root_widget, background_color),
app_driver: Box::new(app_driver),
};
main_state
.app_driver
.on_start(&mut main_state.masonry_state);

event_loop.run_app(&mut main_state)
}

Expand Down
25 changes: 21 additions & 4 deletions masonry/src/render_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,9 @@ impl RenderRoot {
// See https://github.com/linebender/druid/issues/85 for discussion.
let last = self.last_anim.take();
let elapsed_ns = last.map(|t| now.duration_since(t).as_nanos()).unwrap_or(0) as u64;

if self.root_state().request_anim {
let root_state = self.root_state();
if root_state.request_anim {
root_state.request_anim = false;
self.root_lifecycle(LifeCycle::AnimFrame(elapsed_ns));
self.last_anim = Some(now);
}
Expand All @@ -224,16 +225,32 @@ impl RenderRoot {
self.root_on_text_event(event)
}

/// Registers all fonts that exist in the given data.
///
/// Returns a list of pairs each containing the family identifier and fonts
/// added to that family.
pub fn register_fonts(
&mut self,
data: Vec<u8>,
) -> Vec<(fontique::FamilyId, Vec<fontique::FontInfo>)> {
self.state.font_context.collection.register_fonts(data)
}

/// Add a font from its raw data for use in tests.
/// The font is added to the fallback chain for Latin scripts.
/// This is expected to be used with
/// [`RenderRootOptions.use_system_fonts = false`](RenderRootOptions::use_system_fonts)
/// to ensure rendering is consistent cross-platform.
///
/// We expect to develop a much more fully-featured font API in the future, but
/// this is necessary for our testing of Masonry.
pub fn add_test_font(
&mut self,
data: Vec<u8>,
) -> Vec<(fontique::FamilyId, Vec<fontique::FontInfo>)> {
let families = self.state.font_context.collection.register_fonts(data);
// TODO: This code doesn't *seem* reasonable
let families = self.register_fonts(data);
// Make sure that all of these fonts are in the fallback chain for the Latin script.
// <https://en.wikipedia.org/wiki/Script_(Unicode)#Latn>
self.state
.font_context
.collection
Expand Down
1 change: 1 addition & 0 deletions masonry/src/text/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ impl<T> TextLayout<T> {
///
/// This does not account for things like the text changing, handling that
/// is the responsibility of the user.
#[must_use = "Has no side effects"]
pub fn needs_rebuild(&self) -> bool {
self.needs_layout || self.needs_line_breaks
}
Expand Down
2 changes: 2 additions & 0 deletions masonry/src/widget/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mod sized_box;
mod spinner;
mod split;
mod textbox;
mod variable_label;
mod widget_arena;

pub use self::image::Image;
Expand All @@ -44,6 +45,7 @@ pub use sized_box::SizedBox;
pub use spinner::Spinner;
pub use split::Split;
pub use textbox::Textbox;
pub use variable_label::VariableLabel;
pub use widget_mut::WidgetMut;
pub use widget_pod::WidgetPod;
pub use widget_ref::WidgetRef;
Expand Down
Loading

0 comments on commit 3fd3903

Please sign in to comment.