Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not expose prepare_runs, required_action from Text #81

Merged
merged 6 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/display/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use wrap_lines::{Line, RunPart};

/// Error returned on operations if not ready
///
/// This error is returned if `prepare` must be called.
#[derive(Clone, Copy, Default, Debug, thiserror::Error)]
/// This error is returned if `configure` or `prepare` must be called.
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, thiserror::Error)]
#[error("not ready")]
pub struct NotReady;

Expand Down Expand Up @@ -100,7 +100,7 @@ pub struct TextDisplay {
//
/// Level runs within the text, in logical order
runs: SmallVec<[shaper::GlyphRun; 1]>,
pub(crate) action: Action,
action: Action,
/// Contiguous runs, in logical order
///
/// Within a line, runs may not be in visual order due to BIDI reversals.
Expand Down
14 changes: 3 additions & 11 deletions src/display/text_runs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ impl TextDisplay {
/// Prerequisites: prepared runs: requires action is no greater than `Action::Wrap`.
/// Post-requirements: prepare lines (requires action `Action::Wrap`).
/// Parameters: see [`crate::Environment`] documentation.
pub(crate) fn resize_runs<F: FormattableText + ?Sized>(&mut self, text: &F, dpem: f32) {
assert!(self.action <= Action::Resize);
pub fn resize_runs<F: FormattableText + ?Sized>(&mut self, text: &F, dpem: f32) {
assert_eq!(self.action, Action::Resize);
self.action = Action::Wrap;

let mut font_tokens = text.font_tokens(dpem);
Expand Down Expand Up @@ -74,10 +74,6 @@ impl TextDisplay {
/// This is the first step of preparation: breaking text into runs according
/// to font properties, bidi-levels and line-wrap points.
///
/// This method only updates self as required; use [`Self::require_action`] if necessary.
/// On [`Action::All`], this prepares runs from scratch; on [`Action::Resize`] existing runs
/// are resized; afterwards, action is no greater than [`Action::Wrap`].
///
/// Parameters: see [`crate::Environment`] documentation.
pub fn prepare_runs<F: FormattableText + ?Sized>(
&mut self,
Expand All @@ -86,11 +82,7 @@ impl TextDisplay {
mut font_id: FontId,
mut dpem: f32,
) -> Result<(), InvalidFontId> {
match self.action {
Action::None | Action::VAlign | Action::Wrap => return Ok(()),
Action::Resize => return Ok(self.resize_runs(text, dpem)),
Action::All => (),
}
assert_eq!(self.action, Action::All);

// This method constructs a list of "hard lines" (the initial line and any
// caused by a hard break), each composed of a list of "level runs" (the
Expand Down
19 changes: 10 additions & 9 deletions src/display/wrap_lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,14 @@ struct PartInfo {
}

impl TextDisplay {
/// Measure the maximum line length without wrapping
/// Measure required width, up to some `max_width`
///
/// This is a significantly faster way to calculate the required line length
/// than [`Self::prepare_lines`].
/// This method allows calculation of the width requirement of a text object
/// without full wrapping and glyph placement. Whenever the requirement
/// exceeds `max_width`, the algorithm stops early, returning `max_width`.
///
/// The return value is at most `limit` and is unaffected by alignment and
/// wrap configuration of [`crate::Environment`].
pub fn measure_width(&self, limit: f32) -> Result<f32, NotReady> {
/// The return value is unaffected by alignment and wrap configuration.
pub fn measure_width(&self, max_width: f32) -> Result<f32, NotReady> {
if self.action > Action::Wrap {
return Err(NotReady);
}
Expand All @@ -62,8 +62,8 @@ impl TextDisplay {

if part_len_no_space > 0.0 {
line_len = caret + part_len_no_space;
if line_len >= limit {
return Ok(limit);
if line_len >= max_width {
return Ok(max_width);
}
}
caret += part_len;
Expand Down Expand Up @@ -219,7 +219,7 @@ impl TextDisplay {
{
self.num_glyphs = adder.num_glyphs;
}
self.l_bound = adder.l_bound;
self.l_bound = adder.l_bound.min(adder.r_bound);
self.r_bound = bounding_corner.0;
Ok(bounding_corner)
}
Expand Down Expand Up @@ -278,6 +278,7 @@ struct LineAdder {
impl LineAdder {
fn new(bounds: Vec2, align: (Align, Align)) -> Self {
LineAdder {
l_bound: bounds.0,
halign: align.0,
width_bound: bounds.0,
..Default::default()
Expand Down
131 changes: 71 additions & 60 deletions src/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
use crate::display::{Effect, MarkerPosIter, NotReady, TextDisplay};
use crate::fonts::{self, FaceId, InvalidFontId};
use crate::format::{EditableText, FormattableText};
use crate::{Action, Glyph, Vec2};
use crate::{Align, Environment};
use crate::{Action, Align, Environment, Glyph, Vec2};
use std::ops::{Deref, DerefMut};

/// Text, prepared for display in a given environment
///
Expand Down Expand Up @@ -46,6 +46,18 @@ impl<T: FormattableText> Text<T> {
}
}

/// Construct from parts
#[inline]
pub fn from_parts(env: Environment, display: TextDisplay, text: T) -> Self {
Text { env, display, text }
}

/// Decompose into parts
#[inline]
pub fn into_parts(self) -> (Environment, TextDisplay, T) {
(self.env, self.display, self.text)
}

/// Clone the formatted text
pub fn clone_text(&self) -> T
where
Expand Down Expand Up @@ -78,7 +90,7 @@ impl<T: FormattableText> Text<T> {
*/

self.text = text;
self.display.action = Action::All;
self.display.require_action(Action::All);
}

/// Set the text and prepare (if any fonts are loaded)
Expand All @@ -97,6 +109,24 @@ impl<T: FormattableText> Text<T> {
}
}

impl<T: FormattableText + ?Sized> Text<T> {
#[inline]
fn prepare_runs(&mut self) -> Result<(), InvalidFontId> {
match self.display.required_action() {
Action::All => self.display.prepare_runs(
&self.text,
self.env.direction,
self.env.font_id,
self.env.dpem,
)?,
Action::Resize => self.display.resize_runs(&self.text, self.env.dpem),
_ => (),
}

Ok(())
}
}

/// Trait over a sub-set of [`Text`] functionality
///
/// This allows dynamic dispatch over [`Text`]'s type parameters.
Expand Down Expand Up @@ -134,42 +164,28 @@ pub trait TextApi {
/// Read the [`TextDisplay`]
fn display(&self) -> &TextDisplay;

/// Require an action
///
/// See [`TextDisplay::require_action`].
fn require_action(&mut self, action: Action);

/// Prepare text runs
/// Measure required width, up to some `max_width`
///
/// Wraps [`TextDisplay::prepare_runs`], passing parameters from the
/// environment state.
fn prepare_runs(&mut self) -> Result<(), InvalidFontId>;

/// Measure required width, up to some `limit`
/// This method partially prepares the [`TextDisplay`] as required.
///
/// This calls [`Self::prepare_runs`] where necessary, but does not fully
/// prepare text for display. It is a significantly faster way to calculate
/// the required line length than by fully preparing text.
/// This method allows calculation of the width requirement of a text object
/// without full wrapping and glyph placement. Whenever the requirement
/// exceeds `max_width`, the algorithm stops early, returning `max_width`.
///
/// The return value is at most `limit` and is unaffected by alignment and
/// wrap configuration of [`Environment`].
fn measure_width(&mut self, limit: f32) -> Result<f32, InvalidFontId>;
/// The return value is unaffected by alignment and wrap configuration.
fn measure_width(&mut self, max_width: f32) -> Result<f32, InvalidFontId>;

/// Measure required vertical height, wrapping as configured
///
/// This partially prepares text for display. Remaining prepartion should be
/// fast.
/// This method performs most required preparation steps of the
/// [`TextDisplay`]. Remaining prepartion should be fast.
fn measure_height(&mut self) -> Result<f32, InvalidFontId>;

/// Prepare text for display, as necessary
///
/// Does all preparation steps necessary in order to display or query the
/// layout of this text.
///
/// Required preparation actions are tracked internally, but cannot
/// notice changes in the environment. In case the environment has changed
/// one should either call [`TextDisplay::require_action`] before this method.
///
/// Returns true if at least some action is performed *and* the text exceeds
/// the allocated bounds ([`Environment::bounds`]).
fn prepare(&mut self) -> Result<bool, InvalidFontId>;
Expand Down Expand Up @@ -228,27 +244,10 @@ impl<T: FormattableText + ?Sized> TextApi for Text<T> {
self.env = env;
}

#[inline]
fn require_action(&mut self, action: Action) {
self.display.require_action(action);
}

#[inline]
fn prepare_runs(&mut self) -> Result<(), InvalidFontId> {
self.display.prepare_runs(
&self.text,
self.env.direction,
self.env.font_id,
self.env.dpem,
)
}

fn measure_width(&mut self, limit: f32) -> Result<f32, InvalidFontId> {
if self.display.required_action() > Action::Wrap {
self.prepare_runs()?;
}
fn measure_width(&mut self, max_width: f32) -> Result<f32, InvalidFontId> {
self.prepare_runs()?;

Ok(self.display.measure_width(limit).unwrap())
Ok(self.display.measure_width(max_width).unwrap())
}

fn measure_height(&mut self) -> Result<f32, InvalidFontId> {
Expand All @@ -258,11 +257,9 @@ impl<T: FormattableText + ?Sized> TextApi for Text<T> {
self.display.require_action(Action::VAlign);
}

let action = self.display.required_action();
if action > Action::Wrap {
self.prepare_runs()?;
}
self.prepare_runs()?;

let action = self.display.required_action();
let height = if action >= Action::Wrap {
self.display
.prepare_lines(self.env.bounds, self.env.wrap, self.env.align)
Expand Down Expand Up @@ -313,6 +310,12 @@ impl<T: FormattableText + ?Sized> TextApi for Text<T> {

/// Extension trait over [`TextApi`]
pub trait TextApiExt: TextApi {
/// Check whether the text is fully prepared and ready for usage
#[inline]
fn is_prepared(&self) -> bool {
self.display().required_action().is_ready()
}

/// Update the environment and do full preparation
///
/// Fully prepares the text. This is equivalent to calling
Expand Down Expand Up @@ -345,12 +348,6 @@ pub trait TextApiExt: TextApi {
self.display().bounding_box()
}

/// Get required action
#[inline]
fn required_action(&self) -> Action {
self.display().action
}

/// Get the number of lines (after wrapping)
///
/// See [`TextDisplay::num_lines`].
Expand Down Expand Up @@ -518,25 +515,39 @@ impl<T: EditableText + ?Sized> EditableTextApi for Text<T> {
#[inline]
fn insert_char(&mut self, index: usize, c: char) {
self.text.insert_char(index, c);
self.display.action = Action::All;
self.display.require_action(Action::All);
}

#[inline]
fn replace_range(&mut self, range: std::ops::Range<usize>, replace_with: &str) {
self.text.replace_range(range, replace_with);
self.display.action = Action::All;
self.display.require_action(Action::All);
}

#[inline]
fn set_string(&mut self, string: String) {
self.text.set_string(string);
self.display.action = Action::All;
self.display.require_action(Action::All);
}

#[inline]
fn swap_string(&mut self, string: &mut String) {
self.text.swap_string(string);
self.display.action = Action::All;
self.display.require_action(Action::All);
}
}

impl<T: FormattableText + ?Sized> Deref for Text<T> {
type Target = TextDisplay;

fn deref(&self) -> &TextDisplay {
&self.display
}
}

impl<T: FormattableText + ?Sized> DerefMut for Text<T> {
fn deref_mut(&mut self) -> &mut TextDisplay {
&mut self.display
}
}

Expand Down
Loading