diff --git a/CHANGELOG.md b/CHANGELOG.md index 488f657b7..31e8fefba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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). +## Unreleased + +- Merge fns `DrawCx::text_effects` and `text` (#458) +- Remove feature `min_spec` (#458) + ## [0.15.0] — 2024-12-02 Most significant is the addition of the `Collection` trait representing a list or tuple of widgets. diff --git a/Cargo.toml b/Cargo.toml index 81a254428..cf777e648 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,17 +33,15 @@ default = ["minimal", "view", "image", "resvg", "clipboard", "markdown", "shapin # All standard test target features stable = ["default", "x11", "serde", "toml", "yaml", "json", "ron", "macros_log"] # Enables all "recommended" features for nightly rustc -nightly = ["stable", "min_spec"] +nightly = ["stable"] # Additional, less recommendation-worthy features experimental = ["dark-light", "recursive-layout-widgets", "unsafe_node"] # Enable dynamic linking (faster linking via an extra run-time dependency): dynamic = ["dep:kas-dylib"] -# Use min_specialization (enables access key underlining for AccessLabel) -min_spec = ["kas-widgets/min_spec"] # Use full specialization -spec = ["min_spec", "kas-core/spec"] +spec = ["kas-core/spec"] ######### optional dependencies / features ######### diff --git a/README.md b/README.md index e086c7606..a3833b319 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,21 @@ See [Cargo.toml](https://github.com/kas-gui/kas/blob/master/Cargo.toml#L22). [kas]: https://docs.rs/kas +Size +---- + +To reduce binary size, add this to your `Cargo.toml`: +```toml +[profile.release] +strip = true +opt-level = "z" +``` + +You might also consider using feature `dynamic` if wishing to ship multiple +binaries with shared libraries (Rust's `libstd` and `libkas_dylib`); note +however that these are not ABI-stable. + + Copyright and Licence --------------------- diff --git a/crates/kas-core/src/draw/draw_shared.rs b/crates/kas-core/src/draw/draw_shared.rs index 17bfa2b15..8abbdbc18 100644 --- a/crates/kas-core/src/draw/draw_shared.rs +++ b/crates/kas-core/src/draw/draw_shared.rs @@ -186,7 +186,10 @@ pub trait DrawSharedImpl: Any { /// Draw text with a colour and effects /// /// The effects list does not contain colour information, but may contain - /// underlining/strikethrough information. It may be empty. + /// underlining/strikethrough information. + /// + /// If `effects` is empty or all [`Effect::flags`] are default then it is + /// equivalent (and faster) to call [`Self::draw_text`] instead. fn draw_text_effects( &mut self, draw: &mut Self::Draw, @@ -202,6 +205,9 @@ pub trait DrawSharedImpl: Any { /// The `effects` list provides both underlining and colour information. /// If the `effects` list is empty or the first entry has `start > 0`, a /// default entity will be assumed. + /// + /// If `effects` is empty then it is + /// equivalent (and faster) to call [`Self::draw_text`] instead. fn draw_text_effects_rgba( &mut self, draw: &mut Self::Draw, diff --git a/crates/kas-core/src/theme/draw.rs b/crates/kas-core/src/theme/draw.rs index d13d4b8b0..653f430b6 100644 --- a/crates/kas-core/src/theme/draw.rs +++ b/crates/kas-core/src/theme/draw.rs @@ -202,35 +202,26 @@ impl<'a> DrawCx<'a> { self.h.selection(rect, style); } - /// Draw text - /// - /// Text is drawn from `rect.pos` and clipped to `rect`. If the text - /// scrolls, `rect` should be the size of the whole text, not the window. - /// - /// [`ConfigCx::text_configure`] should be called prior to this method to - /// select a font, font size and wrap options (based on the [`TextClass`]). - pub fn text(&mut self, rect: Rect, text: &Text) { - if let Ok(display) = text.display() { - self.h.text(&self.id, rect, display, text.class()); - } - } - /// Draw text with effects /// /// Text is drawn from `rect.pos` and clipped to `rect`. If the text /// scrolls, `rect` should be the size of the whole text, not the window. /// - /// [`Self::text`] already supports *font* effects: bold, - /// emphasis, text size. In addition, this method supports underline and - /// strikethrough effects. + /// This method supports a number of text effects: bold, emphasis, text + /// size, underline and strikethrough. /// /// [`ConfigCx::text_configure`] should be called prior to this method to /// select a font, font size and wrap options (based on the [`TextClass`]). - pub fn text_effects(&mut self, rect: Rect, text: &Text) { + pub fn text(&mut self, rect: Rect, text: &Text) { let effects = text.effect_tokens(); let class = text.class(); - if let Ok(text) = text.display() { - self.h.text_effects(&self.id, rect, text, effects, class); + if let Ok(display) = text.display() { + if effects.is_empty() { + // Use the faster and simpler implementation when we don't have effects + self.h.text(&self.id, rect, display, class); + } else { + self.h.text_effects(&self.id, rect, display, effects, class); + } } } @@ -427,6 +418,13 @@ pub trait ThemeDraw { /// emphasis, text size. In addition, this method supports underline and /// strikethrough effects. /// + /// If `effects` is empty or all [`Effect::flags`] are default then it is + /// equivalent (and faster) to call [`Self::text`] instead. + /// + /// Special effect: if `class` is [`TextClass::AccessLabel(_)`] then + /// underline and strikethrough are only drawn if + /// [`EventState::show_access_labels`]. + /// /// [`ConfigCx::text_configure`] should be called prior to this method to /// select a font, font size and wrap options (based on the [`TextClass`]). fn text_effects( diff --git a/crates/kas-wgpu/src/draw/text_pipe.rs b/crates/kas-wgpu/src/draw/text_pipe.rs index bb872d622..7a848622e 100644 --- a/crates/kas-wgpu/src/draw/text_pipe.rs +++ b/crates/kas-wgpu/src/draw/text_pipe.rs @@ -288,17 +288,6 @@ impl Window { effects: &[Effect<()>], mut draw_quad: impl FnMut(Quad), ) { - // Optimisation: use cheaper TextDisplay::glyphs method - if effects.len() <= 1 - && effects - .first() - .map(|e| e.flags == Default::default()) - .unwrap_or(true) - { - self.text(pipe, pass, rect, text, col); - return; - } - let rect = Quad::conv(rect); let mut for_glyph = |face: FaceId, dpem: f32, glyph: Glyph, _: usize, _: ()| { diff --git a/crates/kas-widgets/Cargo.toml b/crates/kas-widgets/Cargo.toml index c900af591..01beb8540 100644 --- a/crates/kas-widgets/Cargo.toml +++ b/crates/kas-widgets/Cargo.toml @@ -13,13 +13,9 @@ repository = "https://github.com/kas-gui/kas" exclude = ["/screenshots"] [package.metadata.docs.rs] -features = ["min_spec", "kas/winit", "kas/wayland"] +features = ["kas/winit", "kas/wayland"] rustdoc-args = ["--cfg", "docsrs"] -[features] -# Use min_specialization (enables access key underlining for AccessLabel) -min_spec = [] - [dependencies] log = "0.4" smallvec = "1.6.1" diff --git a/crates/kas-widgets/src/label.rs b/crates/kas-widgets/src/label.rs index 15825c1ff..5a6d9a6e6 100644 --- a/crates/kas-widgets/src/label.rs +++ b/crates/kas-widgets/src/label.rs @@ -122,13 +122,8 @@ impl_scope! { cx.text_set_size(&mut self.text, rect.size, align); } - #[cfg(feature = "min_spec")] - default fn draw(&mut self, mut draw: DrawCx) { - draw.text_effects(self.rect(), &self.text); - } - #[cfg(not(feature = "min_spec"))] fn draw(&mut self, mut draw: DrawCx) { - draw.text_effects(self.rect(), &self.text); + draw.text(self.rect(), &self.text); } } @@ -155,20 +150,6 @@ impl_scope! { } } -// Str/String representations have no effects, so use simpler draw call -#[cfg(feature = "min_spec")] -impl<'a> Layout for Label<&'a str> { - fn draw(&mut self, mut draw: DrawCx) { - draw.text(self.rect(), &self.text); - } -} -#[cfg(feature = "min_spec")] -impl Layout for Label { - fn draw(&mut self, mut draw: DrawCx) { - draw.text(self.rect(), &self.text); - } -} - /* TODO(specialization): can we support this? min_specialization is not enough. impl + FormattableText + 'static> From for Label { default fn from(text: U) -> Self { @@ -296,7 +277,7 @@ impl_scope! { } fn draw(&mut self, mut draw: DrawCx) { - draw.text_effects(self.rect(), &self.text); + draw.text(self.rect(), &self.text); } } diff --git a/crates/kas-widgets/src/lib.rs b/crates/kas-widgets/src/lib.rs index 954b98ebd..2a80006fc 100644 --- a/crates/kas-widgets/src/lib.rs +++ b/crates/kas-widgets/src/lib.rs @@ -59,7 +59,6 @@ //! - [`GripPart`]: a handle (e.g. for a slider, splitter or scroll_bar) #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#![cfg_attr(feature = "min_spec", feature(min_specialization))] pub mod adapt; #[doc(no_inline)] diff --git a/crates/kas-widgets/src/scroll_bar.rs b/crates/kas-widgets/src/scroll_bar.rs index c27263c9e..315623d3a 100644 --- a/crates/kas-widgets/src/scroll_bar.rs +++ b/crates/kas-widgets/src/scroll_bar.rs @@ -399,18 +399,6 @@ impl_scope! { pub fn inner_mut(&mut self) -> &mut W { &mut self.inner } - - fn draw_(&mut self, mut draw: DrawCx) { - draw.recurse(&mut self.inner); - draw.with_pass(|mut draw| { - if self.show_bars.0 { - draw.recurse(&mut self.horiz_bar); - } - if self.show_bars.1 { - draw.recurse(&mut self.vert_bar); - } - }); - } } impl HasScrollBars for Self { @@ -523,25 +511,9 @@ impl_scope! { .or_else(|| Some(self.id())) } - #[cfg(feature = "min_spec")] - default fn draw(&mut self, draw: DrawCx) { - self.draw_(draw); - } - #[cfg(not(feature = "min_spec"))] - fn draw(&mut self, draw: DrawCx) { - self.draw_(draw); - } - } - - #[cfg(feature = "min_spec")] - impl Layout for ScrollBars> { fn draw(&mut self, mut draw: DrawCx) { - // Enlarge clip region to *our* rect: - draw.with_clip_region(self.core.rect, self.inner.scroll_offset(), |mut draw| { - draw.recurse(&mut self.inner); - }); - // Use a second clip region to force draw order: - draw.with_clip_region(self.core.rect, Offset::ZERO, |mut draw| { + draw.recurse(&mut self.inner); + draw.with_pass(|mut draw| { if self.show_bars.0 { draw.recurse(&mut self.horiz_bar); } diff --git a/crates/kas-widgets/src/tab_stack.rs b/crates/kas-widgets/src/tab_stack.rs index 65ccfc25f..08c09ab33 100644 --- a/crates/kas-widgets/src/tab_stack.rs +++ b/crates/kas-widgets/src/tab_stack.rs @@ -7,7 +7,6 @@ use crate::adapt::{AdaptEvents, AdaptWidget}; use crate::{AccessLabel, Row, Stack}; -use kas::layout::{FrameStorage, Visitor}; use kas::messages::Select; use kas::prelude::*; use kas::theme::FrameStyle; @@ -23,13 +22,12 @@ impl_scope! { #[autoimpl(HasStr using self.label)] #[widget { Data = (); - layout = button!(self.label); + layout = frame!(self.label, style = FrameStyle::Tab); navigable = true; hover_highlight = true; }] pub struct Tab { core: widget_core!(), - frame: FrameStorage, #[widget] label: AccessLabel, } @@ -40,32 +38,15 @@ impl_scope! { pub fn new(label: impl Into) -> Self { Tab { core: Default::default(), - frame: FrameStorage::default(), label: AccessLabel::new(label), } } } impl Layout for Self { - fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules { - let label = Visitor::single(&mut self.label); - Visitor::frame(&mut self.frame, label, FrameStyle::Tab).size_rules(sizer, axis) - } - - fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints) { - self.core.rect = rect; - let label = Visitor::single(&mut self.label); - Visitor::frame(&mut self.frame, label, FrameStyle::Tab).set_rect(cx, rect, hints) - } - fn find_id(&mut self, coord: Coord) -> Option { self.rect().contains(coord).then_some(self.id()) } - - fn draw(&mut self, draw: DrawCx) { - let label = Visitor::single(&mut self.label); - Visitor::frame(&mut self.frame, label, FrameStyle::Tab).draw(draw) - } } impl Events for Self { diff --git a/crates/kas-widgets/src/text.rs b/crates/kas-widgets/src/text.rs index b3bb295ed..f97cc69fd 100644 --- a/crates/kas-widgets/src/text.rs +++ b/crates/kas-widgets/src/text.rs @@ -115,13 +115,8 @@ impl_scope! { cx.text_set_size(&mut self.text, rect.size, align); } - #[cfg(feature = "min_spec")] - default fn draw(&mut self, mut draw: DrawCx) { - draw.text_effects(self.rect(), &self.text); - } - #[cfg(not(feature = "min_spec"))] fn draw(&mut self, mut draw: DrawCx) { - draw.text_effects(self.rect(), &self.text); + draw.text(self.rect(), &self.text); } } @@ -146,20 +141,6 @@ impl_scope! { } } -// Str/String representations have no effects, so use simpler draw call -#[cfg(feature = "min_spec")] -impl<'a, A> Layout for Text { - fn draw(&mut self, mut draw: DrawCx) { - draw.text(self.rect(), &self.text); - } -} -#[cfg(feature = "min_spec")] -impl Layout for Text { - fn draw(&mut self, mut draw: DrawCx) { - draw.text(self.rect(), &self.text); - } -} - /* TODO(specialization): can we support this? min_specialization is not enough. impl + FormattableText + 'static> From for Text { default fn from(text: U) -> Self { diff --git a/examples/mandlebrot/mandlebrot.rs b/examples/mandlebrot/mandlebrot.rs index f210a306c..892677f22 100644 --- a/examples/mandlebrot/mandlebrot.rs +++ b/examples/mandlebrot/mandlebrot.rs @@ -13,7 +13,7 @@ use kas::event::{self, Command}; use kas::geom::{DVec2, Vec2, Vec3}; use kas::prelude::*; use kas::widgets::adapt::Reserve; -use kas::widgets::{format_data, format_value, Label, Slider, Text}; +use kas::widgets::{format_value, Label, Slider, Text}; use kas_wgpu::draw::{CustomPipe, CustomPipeBuilder, CustomWindow, DrawCustom, DrawPipe}; use kas_wgpu::wgpu; use std::mem::size_of; @@ -286,8 +286,8 @@ impl PipeWindow { } } -#[derive(Clone, Debug)] -struct ViewUpdate; +#[derive(Debug)] +struct ViewUpdate(String); impl_scope! { #[widget] @@ -392,7 +392,6 @@ impl_scope! { self.delta += self.alpha.complex_mul(delta); } } - cx.push(ViewUpdate); } Event::Scroll(delta) => { let factor = match delta { @@ -400,7 +399,6 @@ impl_scope! { event::ScrollDelta::PixelDelta(coord) => -0.01 * coord.1 as f64, }; self.alpha = self.alpha * 2f64.powf(factor); - cx.push(ViewUpdate); } Event::Pan { alpha, delta } => { // Our full transform (from screen coordinates to world coordinates) is: @@ -416,8 +414,6 @@ impl_scope! { self.delta = self.delta - new_alpha.complex_mul(delta) * self.view_alpha + (self.alpha - new_alpha).complex_mul(self.view_delta); self.alpha = new_alpha; - - cx.push(ViewUpdate); } Event::PressStart { press } => { return press.grab(self.id()) @@ -427,6 +423,9 @@ impl_scope! { } _ => return Unused, } + + cx.redraw(self.id()); + cx.push(ViewUpdate(self.loc())); Used } } @@ -440,13 +439,14 @@ impl_scope! { (3, 0) => self.buttons, (0, 1) => self.iters_label.align(AlignHints::CENTER), (0, 2) => self.slider, + // extra col span allows use of Label's margin (1..5, 1..4) => self.mbrot, }; }] struct MandlebrotUI { core: widget_core!(), - #[widget(&self.mbrot)] - label: Text, + #[widget(&self.loc)] + label: Text, #[widget] title: Label<&'static str>, #[widget] @@ -455,25 +455,28 @@ impl_scope! { iters_label: Reserve>, #[widget(&self.iters)] slider: Slider, - // extra col span allows use of Label's margin #[widget(&self.iters)] mbrot: Mandlebrot, iters: i32, + loc: String, } impl MandlebrotUI { fn new() -> MandlebrotUI { + let mbrot = Mandlebrot::new(); + let loc = mbrot.loc(); MandlebrotUI { core: Default::default(), - label: format_data!(mbrot: &Mandlebrot, "{}", mbrot.loc()), + label: format_value!("{}"), title: Label::new("Mandlebrot"), buttons: Default::default(), iters_label: format_value!("{}") .with_min_size_em(3.0, 0.0), slider: Slider::up(0..=256, |_, iters| *iters) .with_msg(|iters| iters), - mbrot: Mandlebrot::new(), + mbrot, iters: 64, + loc, } } } @@ -483,8 +486,8 @@ impl_scope! { fn handle_messages(&mut self, cx: &mut EventCx, data: &()) { if let Some(iters) = cx.try_pop() { self.iters = iters; - } else if let Some(ViewUpdate) = cx.try_pop() { - cx.redraw(self.mbrot.id()); + } else if let Some(ViewUpdate(loc)) = cx.try_pop() { + self.loc = loc; } else { return; }