Skip to content

Commit 9797e78

Browse files
authored
Merge pull request #139 from kas-gui/push-ttxpouxzlynv
Replace BiDi level inference: add `GlyphRun::base_level`
2 parents 367ba7e + 7510631 commit 9797e78

File tree

6 files changed

+289
-101
lines changed

6 files changed

+289
-101
lines changed

src/display/mod.rs

Lines changed: 36 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55

66
//! Text prepared for display
77
8-
#[allow(unused)]
9-
use crate::Text;
108
use crate::conv::to_usize;
11-
use crate::{Direction, Vec2, shaper};
9+
#[allow(unused)]
10+
use crate::{Direction, Status, Text};
11+
use crate::{Vec2, shaper};
1212
use smallvec::SmallVec;
1313
use tinyvec::TinyVec;
1414

@@ -37,33 +37,24 @@ pub struct NotReady;
3737
///
3838
/// ### Status of preparation
3939
///
40-
/// Stages of preparation are as follows:
41-
///
42-
/// 1. Ensure all required [fonts](crate::fonts) are loaded.
43-
/// 2. Call [`Self::prepare_runs`] to break text into level runs, then shape
44-
/// these runs into glyph runs (unwrapped but with weak break points).
40+
/// This struct does not track the state-of-preparation internally. It is
41+
/// recommended to use [`Text`] or a custom wrapper to do this. The [`Status`]
42+
/// enum may be helpful here.
4543
///
46-
/// This method must be called again if the `text`, text `direction` or
47-
/// `font_id` change. If only the text size (`dpem`) changes, it is
48-
/// sufficient to instead call [`Self::resize_runs`].
49-
/// 3. Optionally, [`Self::measure_width`] and [`Self::measure_height`] may be
50-
/// used at this point to determine size requirements.
51-
/// 4. Call [`Self::prepare_lines`] to wrap text and perform re-ordering (where
52-
/// lines are bi-directional) and horizontal alignment.
44+
/// Methods note the expected status-of-preparation. Violating this expectation
45+
/// is memory-safe but may cause a panic or an unexpected result.
5346
///
54-
/// This must be called again if any of `wrap_width`, `width_bound` or
55-
/// `h_align` change.
56-
/// 5. Call [`Self::vertically_align`] to set or adjust vertical alignment.
57-
/// (Not technically required if alignment is always top.)
47+
/// Steps of preparation are as follows:
5848
///
59-
/// All methods are idempotent (that is, they may be called multiple times
60-
/// without affecting the result). Later stages of preparation do not affect
61-
/// earlier stages, but if an earlier stage is repeated to account for adjusted
62-
/// configuration then later stages must also be repeated.
63-
///
64-
/// This struct does not track the state of preparation. It is recommended to
65-
/// use [`Text`] or a custom wrapper for that purpose. Failure to observe the
66-
/// correct sequence is memory-safe but may cause panic or an unexpected result.
49+
/// 1. Run-breaking: call [`Self::prepare_runs`] to break text into runs,
50+
/// resolve fonts and apply shaping. (This is the most expensive step,
51+
/// especially when shaping is enabled.)
52+
/// 2. (Optional) Measure size requirements using [`Self::measure_width`] and
53+
/// [`Self::measure_height`].
54+
/// 3. Line-wrapping: call [`Self::prepare_lines`] to perform line-wrapping at
55+
/// the given wrap-width. This also re-orders segments (where lines are
56+
/// bi-directional) and performs horizontal alignment.
57+
/// 4. (Optional) Tweak alignment (e.g. to vertically center text).
6758
///
6859
/// ### Text navigation
6960
///
@@ -85,12 +76,8 @@ pub struct NotReady;
8576
/// which provides a
8677
/// [`GraphemeCursor`](https://unicode-rs.github.io/unicode-segmentation/unicode_segmentation/struct.GraphemeCursor.html)
8778
/// to step back or forward one "grapheme", in logical text order.
88-
/// Optionally, the direction may
89-
/// be reversed for right-to-left lines [`TextDisplay::line_is_rtl`], but note
90-
/// that the result may be confusing since not all text on the line follows the
91-
/// line's base direction and adjacent lines may have different directions.
92-
///
93-
/// Navigating glyphs left or right in display-order is not currently supported.
79+
/// The direction of navigation may be reversed for [right-to-left text](Self::text_is_rtl)
80+
/// (i.e. reversed logical-order navigation).
9481
///
9582
/// To navigate "up" and "down" lines, use [`TextDisplay::text_glyph_pos`] to
9683
/// get the position of the cursor, [`TextDisplay::find_line`] to get the line
@@ -123,10 +110,10 @@ pub struct TextDisplay {
123110
fn size_of_elts() {
124111
use std::mem::size_of;
125112
assert_eq!(size_of::<TinyVec<[u8; 0]>>(), 24);
126-
assert_eq!(size_of::<shaper::GlyphRun>(), 112);
113+
assert_eq!(size_of::<shaper::GlyphRun>(), 120);
127114
assert_eq!(size_of::<RunPart>(), 24);
128115
assert_eq!(size_of::<Line>(), 24);
129-
assert_eq!(size_of::<TextDisplay>(), 200);
116+
assert_eq!(size_of::<TextDisplay>(), 208);
130117
}
131118

132119
impl Default for TextDisplay {
@@ -208,26 +195,18 @@ impl TextDisplay {
208195
first
209196
}
210197

211-
/// Get the base directionality of the text
198+
/// Get the base directionality of the first paragraph
212199
///
213-
/// [Requires status][Self#status-of-preparation]: none.
214-
pub fn text_is_rtl(&self, text: &str, direction: Direction) -> bool {
215-
let (is_auto, mut is_rtl) = match direction {
216-
Direction::Ltr => (false, false),
217-
Direction::Rtl => (false, true),
218-
Direction::Auto => (true, false),
219-
Direction::AutoRtl => (true, true),
220-
};
221-
222-
if is_auto {
223-
match unicode_bidi::get_base_direction(text) {
224-
unicode_bidi::Direction::Ltr => is_rtl = false,
225-
unicode_bidi::Direction::Rtl => is_rtl = true,
226-
unicode_bidi::Direction::Mixed => (),
227-
}
228-
}
229-
230-
is_rtl
200+
/// [Requires status][Self#status-of-preparation]: run-breaking is complete.
201+
///
202+
/// This returns the direction inferred from the `text` and [`Direction`]
203+
/// used during run-breaking. See also [`Direction::text_is_rtl`].
204+
#[inline]
205+
pub fn text_is_rtl(&self) -> bool {
206+
self.runs
207+
.first()
208+
.map(|r| r.base_level.is_rtl())
209+
.unwrap_or_default()
231210
}
232211

233212
/// Get the directionality of the current line
@@ -236,16 +215,16 @@ impl TextDisplay {
236215
///
237216
/// Returns:
238217
///
239-
/// - `None` if text is empty
218+
/// - `None` if the line is not found
240219
/// - `Some(line_is_right_to_left)` otherwise
241220
///
242221
/// Note: indeterminate lines (e.g. empty lines) have their direction
243-
/// determined from the passed environment, by default left-to-right.
222+
/// determined from the passed [`Direction`].
244223
pub fn line_is_rtl(&self, line: usize) -> Option<bool> {
245224
if let Some(line) = self.lines.get(line) {
246225
let first_run = line.run_range.start();
247226
let glyph_run = to_usize(self.wrapped_runs[first_run].glyph_run);
248-
Some(self.runs[glyph_run].level.is_rtl())
227+
Some(self.runs[glyph_run].base_level.is_rtl())
249228
} else {
250229
None
251230
}

0 commit comments

Comments
 (0)