diff --git a/.claude/worktrees/agent-a7ed3fe3 b/.claude/worktrees/agent-a7ed3fe3 new file mode 160000 index 000000000..ab7e53c2f --- /dev/null +++ b/.claude/worktrees/agent-a7ed3fe3 @@ -0,0 +1 @@ +Subproject commit ab7e53c2f16a17943fe3cebbd434fee632c2109a diff --git a/.claude/worktrees/agent-a869dcda b/.claude/worktrees/agent-a869dcda new file mode 160000 index 000000000..3b693c45d --- /dev/null +++ b/.claude/worktrees/agent-a869dcda @@ -0,0 +1 @@ +Subproject commit 3b693c45d3145171f9c62c2b1aa00bdad979b9c5 diff --git a/.gitignore b/.gitignore index 180410f1c..d8595af7c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /.lapce /.idea /.zed +/.claude .DS_Store rustc-ice* *.orig diff --git a/Cargo.lock b/Cargo.lock index 9d1e6e522..861c122e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1551,6 +1551,7 @@ dependencies = [ "floem_reactive", "floem_renderer", "floem_skia_renderer", + "floem_style", "floem_tiny_skia_renderer", "floem_vello_renderer", "floem_vger_renderer", @@ -1601,6 +1602,7 @@ name = "floem-editor-core" version = "0.2.0" dependencies = [ "bitflags 2.11.0", + "floem_style", "itertools 0.14.0", "lapce-xi-rope", "memchr", @@ -1862,6 +1864,7 @@ version = "0.2.0" dependencies = [ "crossbeam", "floem-winit", + "floem_style", "fontique", "futures", "parking_lot", @@ -1888,6 +1891,23 @@ dependencies = [ "resvg", ] +[[package]] +name = "floem_style" +version = "0.2.0" +dependencies = [ + "bitflags 2.11.0", + "imbl", + "parley", + "paste", + "peniko", + "rustc-hash 2.1.1", + "slotmap", + "smallvec", + "taffy", + "unic-langid", + "web-time", +] + [[package]] name = "floem_tiny_skia_renderer" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 5b03934fb..ef0b3b3ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ members = [ "tiny_skia", "reactive", "editor-core", + "style", "examples/*", "ui-events-winit", "test", @@ -21,6 +22,7 @@ default-members = [ "tiny_skia", "reactive", "editor-core", + "style", "ui-events-winit", "test", ] @@ -96,6 +98,7 @@ floem_vello_renderer = { path = "vello", version = "0.2.0", optional = true } floem_vger_renderer = { path = "vger", version = "0.2.0", optional = true } floem_tiny_skia_renderer = { path = "tiny_skia", version = "0.2.0" } floem_reactive = { path = "reactive", version = "0.2.0" } +floem_style = { path = "style", version = "0.2.0" } floem-editor-core = { path = "editor-core", version = "0.2.0", optional = true } copypasta = { version = "0.10", default-features = false, features = [ "wayland", @@ -185,7 +188,7 @@ image-webp = ["image/webp"] tokio = ["dep:tokio"] # rfd (file dialog) async runtime crossbeam = ["dep:crossbeam", "floem_renderer/crossbeam"] -localization = ["dep:fluent-bundle", "dep:unic-langid", "dep:sys-locale", "dep:ouroboros"] +localization = ["dep:fluent-bundle", "dep:unic-langid", "dep:sys-locale", "dep:ouroboros", "floem_style/localization"] [dev-dependencies] criterion = { version = "0.8.1", features = ["html_reports"] } diff --git a/editor-core/Cargo.toml b/editor-core/Cargo.toml index cb9444a37..bab119bd3 100644 --- a/editor-core/Cargo.toml +++ b/editor-core/Cargo.toml @@ -11,6 +11,7 @@ serde = { workspace = true, optional = true } strum = { workspace = true } strum_macros = { workspace = true } ui-events.workspace = true +floem_style = { path = "../style", version = "0.2.0" } lapce-xi-rope = { workspace = true } diff --git a/editor-core/src/indent.rs b/editor-core/src/indent.rs index c2518dd14..7ea37e6fa 100644 --- a/editor-core/src/indent.rs +++ b/editor-core/src/indent.rs @@ -25,6 +25,17 @@ impl std::fmt::Display for IndentStyle { } } +impl floem_style::StylePropValue for IndentStyle {} + +impl floem_style::PropDebugView for IndentStyle { + fn debug_view( + &self, + r: &dyn floem_style::InspectorRender, + ) -> Option> { + Some(r.text(&self.to_string())) + } +} + impl IndentStyle { pub const LONGEST_INDENT: &'static str = " "; // 8 spaces pub const DEFAULT_INDENT: IndentStyle = IndentStyle::Spaces(4); diff --git a/renderer/Cargo.toml b/renderer/Cargo.toml index 4bd8c0c8b..08bdfc11b 100644 --- a/renderer/Cargo.toml +++ b/renderer/Cargo.toml @@ -16,6 +16,7 @@ winit = { workspace = true } wgpu = { workspace = true } crossbeam = { version = "0.8", optional = true } futures = "0.3.32" +floem_style = { path = "../style", version = "0.2.0" } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen-futures = { version = "0.4" } diff --git a/renderer/src/text/attrs.rs b/renderer/src/text/attrs.rs index 3096964ca..406f06ed0 100644 --- a/renderer/src/text/attrs.rs +++ b/renderer/src/text/attrs.rs @@ -106,52 +106,7 @@ impl FamilyOwned { } } -/// Specifies how line height is computed for text layout. -/// -/// # Example -/// -/// ``` -/// use floem_renderer::text::{Attrs, LineHeightValue}; -/// -/// // 1.5x the font size (e.g. 24px for a 16px font). -/// let attrs = Attrs::new().line_height(LineHeightValue::Normal(1.5)); -/// -/// // Fixed 20-point line height regardless of font size. -/// let attrs = Attrs::new().line_height(LineHeightValue::Pt(20.0)); -/// ``` -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum LineHeightValue { - /// A multiplier of the font size (e.g. `1.0` means line height equals font size). - Normal(f32), - /// An absolute line height in points. - Pt(f32), -} -impl LineHeightValue { - pub fn resolve(&self, font_size: f32) -> f32 { - match self { - LineHeightValue::Pt(value) => *value, - LineHeightValue::Normal(multiplier) => font_size * multiplier, - } - } -} - -impl From for LineHeightValue { - fn from(value: f32) -> Self { - LineHeightValue::Normal(value) - } -} - -impl From for LineHeightValue { - fn from(value: f64) -> Self { - LineHeightValue::Normal(value as f32) - } -} - -impl From for LineHeightValue { - fn from(value: i32) -> Self { - LineHeightValue::Normal(value as f32) - } -} +pub use floem_style::LineHeightValue; /// Text styling attributes used to configure font properties, color, and layout. /// diff --git a/src/animate/mod.rs b/src/animate/mod.rs index c013f2622..d02968a9b 100644 --- a/src/animate/mod.rs +++ b/src/animate/mod.rs @@ -1,672 +1,246 @@ #![deny(missing_docs)] //! Animations - -pub mod easing; - +//! +//! This module is a thin reactive wrapper around the engine animation +//! types that live in [`floem_style::animation`]. The engine owns +//! keyframes, the state machine, interpolation math, and easings; this +//! wrapper adds floem-specific pieces the engine can't depend on — +//! reactive triggers for lifecycle callbacks, `RwSignal`-backed command +//! wiring, and `ViewId` routing for imperative `.start()` / `.pause()` / +//! `.state()` controls. + +pub use floem_style::easing; pub use easing::{Bezier, Easing, Linear, Spring, Step, StepPosition}; -use crate::{ - ViewId, - style::{Style, StylePropRef}, - unit::UnitExt, - view::StackOffset, +pub use floem_style::{ + AnimStateCommand, AnimStateKind, AnimationEvents, KeyFrame, KeyFrameStyle, PropCache, + RepeatMode, ReverseOnce, }; -use std::any::Any; -use std::rc::Rc; +use crate::{ViewId, view::StackOffset}; -use crate::platform::{Duration, Instant}; use floem_reactive::{RwSignal, SignalGet, Trigger, UpdaterEffect}; -use smallvec::{SmallVec, smallvec}; - -/// Holds a resolved prop, along with the associated frame id and easing function -#[derive(Clone, Debug)] -pub struct KeyFrameProp { - // the style prop value. This will either come from an animation frameor it will be pulled from the computed style - val: Rc, - // the frame id - id: u16, - /// This easing will be used while animating towards this keyframe. while this prop is the lower one this easing function will not be used. - easing: Rc, -} - -/// Defines whether the style in a key frame should be stored in the frame or it it should be pulled from the computed style -#[derive(Clone, Debug)] -pub enum KeyFrameStyle { - /// when computed style, props will be pulled from the computed style - Computed, - /// When using style, the props will be stored in the key frame - Style(Style), -} -impl From