From 5539dbe620f3a184490010a706104de36ba80c3e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 29 Sep 2021 08:45:13 +0200 Subject: [PATCH] Add separate serialize feature and better persitence control (#753) * Rename epaint feature "persistence" to "serialize" * Add separate "serialize" feature to egui * egui_demo_lib: separate serialize and persistence features * Add App::persist_native_window and App::persist_egui_memory Controls what gets persisted --- CHANGELOG.md | 3 +- eframe/CHANGELOG.md | 3 +- egui/Cargo.toml | 10 ++++-- egui/src/any/serializable/type_id.rs | 2 +- egui/src/containers/area.rs | 2 +- egui/src/containers/collapsing_header.rs | 4 +-- egui/src/containers/panel.rs | 2 +- egui/src/containers/resize.rs | 2 +- egui/src/containers/scroll_area.rs | 6 ++-- egui/src/data/input.rs | 20 ++++++------ egui/src/data/output.rs | 10 +++--- egui/src/grid.rs | 2 +- egui/src/id.rs | 2 +- egui/src/layers.rs | 4 +-- egui/src/layout.rs | 6 ++-- egui/src/lib.rs | 2 +- egui/src/memory.rs | 24 +++++++------- egui/src/menu.rs | 4 +-- egui/src/sense.rs | 2 +- egui/src/style.rs | 28 ++++++++-------- egui/src/util/undoer.rs | 6 ++-- egui/src/widgets/plot/mod.rs | 2 +- egui/src/widgets/plot/transform.rs | 2 +- egui/src/widgets/text_edit.rs | 14 ++++---- egui_demo_lib/Cargo.toml | 8 +++-- egui_demo_lib/src/apps/color_test.rs | 4 +-- egui_demo_lib/src/apps/demo/app.rs | 10 +++--- egui_demo_lib/src/apps/demo/code_editor.rs | 6 ++-- .../src/apps/demo/dancing_strings.rs | 4 +-- .../src/apps/demo/demo_app_windows.rs | 16 +++++----- egui_demo_lib/src/apps/demo/layout_test.rs | 8 ++--- .../src/apps/demo/misc_demo_window.rs | 20 ++++++------ egui_demo_lib/src/apps/demo/painting.rs | 4 +-- egui_demo_lib/src/apps/demo/scrolling.rs | 10 +++--- egui_demo_lib/src/apps/demo/sliders.rs | 4 +-- egui_demo_lib/src/apps/demo/tests.rs | 2 +- egui_demo_lib/src/apps/demo/widget_gallery.rs | 4 +-- egui_demo_lib/src/apps/demo/window_options.rs | 2 +- .../src/apps/demo/window_with_panels.rs | 2 +- egui_demo_lib/src/apps/fractal_clock.rs | 4 +-- egui_demo_lib/src/apps/http_app.rs | 10 +++--- egui_demo_lib/src/backend_panel.rs | 12 +++---- .../src/easy_mark/easy_mark_editor.rs | 6 ++-- egui_demo_lib/src/wrap_app.rs | 15 ++++----- egui_glium/src/backend.rs | 32 ++++++++++++------- egui_web/src/backend.rs | 4 ++- epaint/Cargo.toml | 3 +- epaint/src/color.rs | 4 +-- epaint/src/lib.rs | 4 +-- epaint/src/mesh.rs | 4 +-- epaint/src/shadow.rs | 2 +- epaint/src/shape.rs | 6 ++-- epaint/src/stroke.rs | 2 +- epaint/src/tessellator.rs | 4 +-- epaint/src/text/cursor.rs | 8 ++--- epaint/src/text/fonts.rs | 12 +++---- epaint/src/text/text_layout_types.rs | 6 ++-- epi/src/lib.rs | 16 +++++++++- 58 files changed, 226 insertions(+), 194 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90839285..c3f7d0bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,13 +11,14 @@ NOTE: [`eframe`](eframe/CHANGELOG.md), [`egui_web`](egui_web/CHANGELOG.md), [`eg * Add horizontal scrolling support to `ScrollArea` and `Window` (opt-in). * `TextEdit::layouter`: Add custom text layout for e.g. syntax highlighting or WYSIWYG. * `Fonts::layout_job*`: New text layout engine allowing mixing fonts, colors and styles, with underlining and strikethrough. +* Add feature `"serialize"` separatedly from `"persistence"`. ### Changed 🔧 * Label text will now be centered, right-aligned and/or justified based on the layout. * `Hyperlink` will now word-wrap just like a `Label`. * All `Ui`:s must now have a finite `max_rect`. * Deprecated: `max_rect_finite`, `available_size_before_wrap_finite` and `available_rect_before_wrap_finite`. -* `Painter`/`Fonts`: text layout now expect color when creating a `Galley`. You may override that color with `Painter::galley_with_color`. +* `Painter`/`Fonts`: text layout now expect a color when creating a `Galley`. You may override that color with `Painter::galley_with_color`. * MSRV (Minimum Supported Rust Version) is now `1.54.0`. * By default, `DragValue`:s no longer show a tooltip when hovered. Change with `Style::explanation_tooltips`. diff --git a/eframe/CHANGELOG.md b/eframe/CHANGELOG.md index 629b523a..8d870e29 100644 --- a/eframe/CHANGELOG.md +++ b/eframe/CHANGELOG.md @@ -1,11 +1,12 @@ # Changelog for eframe -All notable changes to the `eframe` crate. +All notable changes to the `eframe` and `epi` crates. ## Unreleased * `Frame` now provides `set_decorations` to set whether to show window decorations. * Remove "http" feature (use https://github.com/emilk/ehttp instead!). * Increase native scroll speed. +* Add `App::persist_native_window` and `App::persist_egui_memory` to control what gets persisted. ## 0.14.0 - 2021-08-24 diff --git a/egui/Cargo.toml b/egui/Cargo.toml index d1582acf..499f51de 100644 --- a/egui/Cargo.toml +++ b/egui/Cargo.toml @@ -30,6 +30,9 @@ ron = { version = "0.6.4", optional = true } [features] default = ["default_fonts", "single_threaded"] +# add compatibility with https://crates.io/crates/cint +cint = ["epaint/cint"] + # If set, egui will use `include_bytes!` to bundle some fonts. # If you plan on specifying your own fonts you may disable this feature. default_fonts = ["epaint/default_fonts"] @@ -42,10 +45,11 @@ extra_asserts = ["epaint/extra_asserts"] # Add compatability with https://github.com/kvark/mint mint = ["epaint/mint"] -# add compatibility with https://crates.io/crates/cint -cint = ["epaint/cint"] +# enable persistence of memory (window positions etc). +persistence = ["serde", "epaint/serialize", "ron"] -persistence = ["serde", "epaint/persistence", "ron"] +# implement serde on most types. +serialize = ["serde", "epaint/serialize"] # multi_threaded is only needed if you plan to use the same egui::Context # from multiple threads. It comes with a minor performance impact. diff --git a/egui/src/any/serializable/type_id.rs b/egui/src/any/serializable/type_id.rs index abc377c1..414670ff 100644 --- a/egui/src/any/serializable/type_id.rs +++ b/egui/src/any/serializable/type_id.rs @@ -2,7 +2,7 @@ use std::any::Any; /// We need this because `TypeId` can't be deserialized or serialized directly, but this can be done using hashing. However, there is a small possibility that different types will have intersection by hashes of their type ids. #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TypeId(u64); impl TypeId { diff --git a/egui/src/containers/area.rs b/egui/src/containers/area.rs index e55b9124..ec91371d 100644 --- a/egui/src/containers/area.rs +++ b/egui/src/containers/area.rs @@ -8,7 +8,7 @@ use crate::*; /// State that is persisted between frames #[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub(crate) struct State { /// Last known pos pub pos: Pos2, diff --git a/egui/src/containers/collapsing_header.rs b/egui/src/containers/collapsing_header.rs index 448349e1..18922fbd 100644 --- a/egui/src/containers/collapsing_header.rs +++ b/egui/src/containers/collapsing_header.rs @@ -4,8 +4,8 @@ use crate::{widgets::Label, *}; use epaint::{Shape, TextStyle}; #[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "persistence", serde(default))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(default))] pub(crate) struct State { open: bool, diff --git a/egui/src/containers/panel.rs b/egui/src/containers/panel.rs index 6bf72de8..5cab86db 100644 --- a/egui/src/containers/panel.rs +++ b/egui/src/containers/panel.rs @@ -18,7 +18,7 @@ use std::ops::RangeInclusive; use crate::*; #[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] struct PanelState { rect: Rect, } diff --git a/egui/src/containers/resize.rs b/egui/src/containers/resize.rs index 92d9436c..03069eb0 100644 --- a/egui/src/containers/resize.rs +++ b/egui/src/containers/resize.rs @@ -1,7 +1,7 @@ use crate::*; #[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub(crate) struct State { /// This is the size that the user has picked by dragging the resize handles. /// This may be smaller and/or larger than the actual size. diff --git a/egui/src/containers/scroll_area.rs b/egui/src/containers/scroll_area.rs index a4dfdfbd..39dd1de9 100644 --- a/egui/src/containers/scroll_area.rs +++ b/egui/src/containers/scroll_area.rs @@ -8,8 +8,8 @@ use crate::*; #[derive(Clone, Copy, Debug)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "persistence", serde(default))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(default))] pub(crate) struct State { /// Positive offset means scrolling down/right offset: Vec2, @@ -17,7 +17,7 @@ pub(crate) struct State { show_scroll: [bool; 2], /// Momentum, used for kinetic scrolling - #[cfg_attr(feature = "persistence", serde(skip))] + #[cfg_attr(feature = "serde", serde(skip))] pub vel: Vec2, /// Mouse offset relative to the top of the handle when started moving the handle. diff --git a/egui/src/data/input.rs b/egui/src/data/input.rs index 69ab6752..07ebfd38 100644 --- a/egui/src/data/input.rs +++ b/egui/src/data/input.rs @@ -11,7 +11,7 @@ use crate::emath::*; /// /// All coordinates are in points (logical pixels) with origin (0, 0) in the top left corner. #[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct RawInput { /// How many points (logical pixels) the user scrolled pub scroll_delta: Vec2, @@ -132,7 +132,7 @@ impl RawInput { /// A file about to be dropped into egui. #[derive(Clone, Debug, Default, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct HoveredFile { /// Set by the `egui-winit` backend. pub path: Option, @@ -142,7 +142,7 @@ pub struct HoveredFile { /// A file dropped into egui. #[derive(Clone, Debug, Default, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct DroppedFile { /// Set by the `egui-winit` backend. pub path: Option, @@ -158,7 +158,7 @@ pub struct DroppedFile { /// /// This only covers events that egui cares about. #[derive(Clone, Debug, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Event { /// The integration detected a "copy" event (e.g. Cmd+C). Copy, @@ -217,7 +217,7 @@ pub enum Event { /// Mouse button (or similar for touch input) #[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum PointerButton { /// The primary mouse button is usually the left one. Primary = 0, @@ -233,7 +233,7 @@ pub const NUM_POINTER_BUTTONS: usize = 3; /// State of the modifier keys. These must be fed to egui. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Modifiers { /// Either of the alt keys are down (option ⌥ on Mac). pub alt: bool, @@ -276,7 +276,7 @@ impl Modifiers { /// Many keys are omitted because they are not always physical keys (depending on keyboard language), e.g. `;` and `§`, /// and are therefor unsuitable as keyboard shortcuts if you want your app to be portable. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Key { ArrowDown, ArrowLeft, @@ -383,19 +383,19 @@ impl RawInput { /// this is a `u64` as values of this kind can always be obtained by hashing #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TouchDeviceId(pub u64); /// Unique identification of a touch occurrence (finger or pen or ...). /// A Touch ID is valid until the finger is lifted. /// A new ID is used for the next touch. #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TouchId(pub u64); /// In what phase a touch event is in. #[derive(Clone, Copy, Debug, Eq, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum TouchPhase { /// User just placed a touch point on the touch surface Start, diff --git a/egui/src/data/output.rs b/egui/src/data/output.rs index e3b1d0ec..0ec1a319 100644 --- a/egui/src/data/output.rs +++ b/egui/src/data/output.rs @@ -5,7 +5,7 @@ use crate::WidgetType; /// What egui emits each frame. /// The backend should use this. #[derive(Clone, Default, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Output { /// Set the cursor to this icon. pub cursor_icon: CursorIcon, @@ -89,7 +89,7 @@ impl Output { } #[derive(Clone, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct OpenUrl { pub url: String, /// If `true`, open the url in a new tab. @@ -122,7 +122,7 @@ impl OpenUrl { /// /// Loosely based on . #[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum CursorIcon { /// Normal cursor icon, whatever that is. Default, @@ -244,7 +244,7 @@ impl Default for CursorIcon { /// /// In particular, these events may be useful for accessability, i.e. for screen readers. #[derive(Clone, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum OutputEvent { // A widget was clicked. Clicked(WidgetInfo), @@ -272,7 +272,7 @@ impl std::fmt::Debug for OutputEvent { /// Describes a widget such as a [`crate::Button`] or a [`crate::TextEdit`]. #[derive(Clone, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct WidgetInfo { /// The type of widget this is. pub typ: WidgetType, diff --git a/egui/src/grid.rs b/egui/src/grid.rs index b8489f04..7ad6943e 100644 --- a/egui/src/grid.rs +++ b/egui/src/grid.rs @@ -1,7 +1,7 @@ use crate::*; #[derive(Clone, Debug, Default, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub(crate) struct State { col_widths: Vec, row_heights: Vec, diff --git a/egui/src/id.rs b/egui/src/id.rs index 5e4367bb..407b8101 100644 --- a/egui/src/id.rs +++ b/egui/src/id.rs @@ -26,7 +26,7 @@ /// Then there are widgets that need no identifiers at all, like labels, /// because they have no state nor are interacted with. #[derive(Clone, Copy, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Id(u64); impl Id { diff --git a/egui/src/layers.rs b/egui/src/layers.rs index 7ba1ff66..a0e44ccc 100644 --- a/egui/src/layers.rs +++ b/egui/src/layers.rs @@ -9,7 +9,7 @@ use std::sync::Arc; /// Different layer categories #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum Order { /// Painted behind all floating windows Background, @@ -65,7 +65,7 @@ impl Order { /// An identifier for a paint layer. /// Also acts as an identifier for [`Area`]:s. #[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct LayerId { pub order: Order, pub id: Id, diff --git a/egui/src/layout.rs b/egui/src/layout.rs index fafc8b2e..3678d392 100644 --- a/egui/src/layout.rs +++ b/egui/src/layout.rs @@ -76,8 +76,8 @@ impl Region { /// Layout direction, one of `LeftToRight`, `RightToLeft`, `TopDown`, `BottomUp`. #[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "persistence", serde(rename_all = "snake_case"))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] pub enum Direction { LeftToRight, RightToLeft, @@ -115,7 +115,7 @@ impl Direction { /// }); /// ``` #[derive(Clone, Copy, Debug, PartialEq)] -// #[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +// #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Layout { /// Main axis direction main_dir: Direction, diff --git a/egui/src/lib.rs b/egui/src/lib.rs index 7d05f0e0..3b6e1fa8 100644 --- a/egui/src/lib.rs +++ b/egui/src/lib.rs @@ -527,7 +527,7 @@ pub mod special_emojis { /// The different types of built-in widgets in egui #[derive(Clone, Copy, Debug, PartialEq)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum WidgetType { Label, // TODO: emit Label events Hyperlink, diff --git a/egui/src/memory.rs b/egui/src/memory.rs index 3bbbedb1..ea0052c3 100644 --- a/egui/src/memory.rs +++ b/egui/src/memory.rs @@ -14,8 +14,8 @@ use crate::{any, area, window, Id, InputState, LayerId, Pos2, Rect, Style}; /// If you want to store data for your widgets, you should look at `data`/`data_temp` and /// `id_data`/`id_data_temp` fields, and read the documentation of [`any`] module. #[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "persistence", serde(default))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(default))] pub struct Memory { pub options: Options, @@ -53,23 +53,23 @@ pub struct Memory { /// new fonts that will be applied at the start of the next frame pub(crate) new_font_definitions: Option, - #[cfg_attr(feature = "persistence", serde(skip))] + #[cfg_attr(feature = "serde", serde(skip))] pub(crate) interaction: Interaction, - #[cfg_attr(feature = "persistence", serde(skip))] + #[cfg_attr(feature = "serde", serde(skip))] pub(crate) window_interaction: Option, - #[cfg_attr(feature = "persistence", serde(skip))] + #[cfg_attr(feature = "serde", serde(skip))] pub(crate) drag_value: crate::widgets::drag_value::MonoState, pub(crate) areas: Areas, /// Which popup-window is open (if any)? /// Could be a combo box, color picker, menu etc. - #[cfg_attr(feature = "persistence", serde(skip))] + #[cfg_attr(feature = "serde", serde(skip))] popup: Option, - #[cfg_attr(feature = "persistence", serde(skip))] + #[cfg_attr(feature = "serde", serde(skip))] everything_is_visible: bool, } @@ -77,11 +77,11 @@ pub struct Memory { /// Some global options that you can read and write. #[derive(Clone, Debug, Default)] -#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))] -#[cfg_attr(feature = "persistence", serde(default))] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "serde", serde(default))] pub struct Options { /// The default style for new `Ui`:s. - #[cfg_attr(feature = "persistence", serde(skip))] + #[cfg_attr(feature = "serde", serde(skip))] pub(crate) style: std::sync::Arc