Make attribute spans use ranges and update docs
This commit is contained in:
parent
16f0eb9efe
commit
e4c8d4ba6b
8 changed files with 147 additions and 123 deletions
|
|
@ -24,6 +24,8 @@ use cosmic::{
|
|||
},
|
||||
};
|
||||
use cosmic_text::{
|
||||
Attrs,
|
||||
AttrsList,
|
||||
FontSystem,
|
||||
SwashCache,
|
||||
TextBuffer,
|
||||
|
|
@ -63,11 +65,9 @@ fn main() -> cosmic::iced::Result {
|
|||
pub struct Window {
|
||||
theme: Theme,
|
||||
path_opt: Option<PathBuf>,
|
||||
attrs: Attrs<'static>,
|
||||
buffer: Mutex<TextBuffer<'static>>,
|
||||
cache: Mutex<SwashCache<'static>>,
|
||||
bold: bool,
|
||||
italic: bool,
|
||||
monospaced: bool,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
@ -88,12 +88,12 @@ impl Window {
|
|||
match fs::read_to_string(&path) {
|
||||
Ok(text) => {
|
||||
log::info!("opened '{}'", path.display());
|
||||
buffer.set_text(&text);
|
||||
buffer.set_text(&text, self.attrs);
|
||||
self.path_opt = Some(path);
|
||||
},
|
||||
Err(err) => {
|
||||
log::error!("failed to open '{}': {}", path.display(), err);
|
||||
buffer.set_text("");
|
||||
buffer.set_text("", self.attrs);
|
||||
self.path_opt = None;
|
||||
}
|
||||
}
|
||||
|
|
@ -107,14 +107,13 @@ impl Application for Window {
|
|||
type Theme = Theme;
|
||||
|
||||
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
|
||||
let font_size_i = 1; // Body
|
||||
let attrs = cosmic_text::Attrs::new()
|
||||
.monospaced(true)
|
||||
.family(cosmic_text::Family::Monospace);
|
||||
|
||||
let buffer = TextBuffer::new(
|
||||
&FONT_SYSTEM,
|
||||
attrs,
|
||||
FONT_SIZES[font_size_i],
|
||||
FONT_SIZES[1 /* Body */],
|
||||
);
|
||||
|
||||
let cache = SwashCache::new(&FONT_SYSTEM);
|
||||
|
|
@ -122,11 +121,9 @@ impl Application for Window {
|
|||
let mut window = Window {
|
||||
theme: Theme::Dark,
|
||||
path_opt: None,
|
||||
attrs,
|
||||
buffer: Mutex::new(buffer),
|
||||
cache: Mutex::new(cache),
|
||||
bold: false,
|
||||
italic: false,
|
||||
monospaced: true,
|
||||
};
|
||||
if let Some(arg) = env::args().nth(1) {
|
||||
window.open(PathBuf::from(arg));
|
||||
|
|
@ -157,7 +154,7 @@ impl Application for Window {
|
|||
if let Some(path) = &self.path_opt {
|
||||
let buffer = self.buffer.lock().unwrap();
|
||||
let mut text = String::new();
|
||||
for line in buffer.text_lines() {
|
||||
for line in buffer.lines.iter() {
|
||||
text.push_str(line.text());
|
||||
text.push('\n');
|
||||
}
|
||||
|
|
@ -172,39 +169,42 @@ impl Application for Window {
|
|||
}
|
||||
},
|
||||
Message::Bold(bold) => {
|
||||
self.bold = bold;
|
||||
|
||||
let mut buffer = self.buffer.lock().unwrap();
|
||||
let attrs = buffer.attrs().clone().weight(if bold {
|
||||
self.attrs = self.attrs.weight(if bold {
|
||||
cosmic_text::Weight::BOLD
|
||||
} else {
|
||||
cosmic_text::Weight::NORMAL
|
||||
});
|
||||
buffer.set_attrs(attrs);
|
||||
},
|
||||
Message::Italic(italic) => {
|
||||
self.italic = italic;
|
||||
|
||||
let mut buffer = self.buffer.lock().unwrap();
|
||||
let attrs = buffer.attrs().clone().style(if italic {
|
||||
for line in buffer.lines.iter_mut() {
|
||||
line.set_attrs_list(AttrsList::new(self.attrs));
|
||||
}
|
||||
},
|
||||
Message::Italic(italic) => {
|
||||
self.attrs = self.attrs.style(if italic {
|
||||
cosmic_text::Style::Italic
|
||||
} else {
|
||||
cosmic_text::Style::Normal
|
||||
});
|
||||
buffer.set_attrs(attrs);
|
||||
},
|
||||
Message::Monospaced(monospaced) => {
|
||||
self.monospaced = monospaced;
|
||||
|
||||
let mut buffer = self.buffer.lock().unwrap();
|
||||
let attrs = buffer.attrs().clone()
|
||||
for line in buffer.lines.iter_mut() {
|
||||
line.set_attrs_list(AttrsList::new(self.attrs));
|
||||
}
|
||||
},
|
||||
Message::Monospaced(monospaced) => {
|
||||
self.attrs = self.attrs
|
||||
.family(if monospaced {
|
||||
cosmic_text::Family::Monospace
|
||||
} else {
|
||||
cosmic_text::Family::SansSerif
|
||||
})
|
||||
.monospaced(monospaced);
|
||||
buffer.set_attrs(attrs);
|
||||
|
||||
let mut buffer = self.buffer.lock().unwrap();
|
||||
for line in buffer.lines.iter_mut() {
|
||||
line.set_attrs_list(AttrsList::new(self.attrs));
|
||||
}
|
||||
},
|
||||
Message::MetricsChanged(metrics) => {
|
||||
let mut buffer = self.buffer.lock().unwrap();
|
||||
|
|
@ -246,11 +246,11 @@ impl Application for Window {
|
|||
button!("Save").on_press(Message::Save),
|
||||
horizontal_space(Length::Fill),
|
||||
text("Bold:"),
|
||||
toggler(None, self.bold, Message::Bold),
|
||||
toggler(None, self.attrs.weight == cosmic_text::Weight::BOLD, Message::Bold),
|
||||
text("Italic:"),
|
||||
toggler(None, self.italic, Message::Italic),
|
||||
toggler(None, self.attrs.style == cosmic_text::Style::Italic, Message::Italic),
|
||||
text("Monospaced:"),
|
||||
toggler(None, self.monospaced, Message::Monospaced),
|
||||
toggler(None, self.attrs.monospaced, Message::Monospaced),
|
||||
text("Theme:"),
|
||||
theme_picker,
|
||||
text("Font Size:"),
|
||||
|
|
|
|||
|
|
@ -179,27 +179,6 @@ where
|
|||
}
|
||||
});
|
||||
|
||||
/*
|
||||
// Draw scrollbar
|
||||
{
|
||||
let start_line = start_line_opt.unwrap_or(end_line);
|
||||
let lines = buffer.text_lines().len();
|
||||
let start_y = (start_line.get() * window.height() as usize) / lines;
|
||||
let end_y = (end_line.get() * window.height() as usize) / lines;
|
||||
if end_y > start_y {
|
||||
window.rect(
|
||||
window.width() as i32 - line_x as i32,
|
||||
start_y as i32,
|
||||
line_x as u32,
|
||||
(end_y - start_y) as u32,
|
||||
Color::from_rgba8(0xFF, 0xFF, 0xFF, 0.25),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.redraw = false;
|
||||
*/
|
||||
|
||||
let duration = instant.elapsed();
|
||||
log::trace!("redraw: {:?}", duration);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,12 +78,8 @@ fn main() {
|
|||
let mut font_size_i = font_size_default;
|
||||
|
||||
let line_x = 8 * display_scale;
|
||||
let attrs = Attrs::new()
|
||||
.monospaced(true)
|
||||
.family(Family::Monospace);
|
||||
let mut buffer = TextBuffer::new(
|
||||
&font_system,
|
||||
attrs,
|
||||
font_sizes[font_size_i]
|
||||
);
|
||||
|
||||
|
|
@ -92,7 +88,10 @@ fn main() {
|
|||
window.height() as i32
|
||||
);
|
||||
|
||||
buffer.set_text(&text);
|
||||
let attrs = Attrs::new()
|
||||
.monospaced(true)
|
||||
.family(Family::Monospace);
|
||||
buffer.set_text(&text, attrs);
|
||||
|
||||
let mut bg_color = orbclient::Color::rgb(0x00, 0x00, 0x00);
|
||||
let mut font_color = Color::rgb(0xFF, 0xFF, 0xFF);
|
||||
|
|
@ -176,8 +175,7 @@ fn main() {
|
|||
let mut attrs_list = AttrsList::new(attrs);
|
||||
for (style, _, range) in ranges {
|
||||
attrs_list.add_span(
|
||||
range.start,
|
||||
range.end,
|
||||
range,
|
||||
attrs
|
||||
.color(Color::rgba(
|
||||
style.foreground.r,
|
||||
|
|
@ -242,19 +240,19 @@ fn main() {
|
|||
window.rect(line_x + x, y, w, h, orbclient::Color { data: color.0 })
|
||||
});
|
||||
|
||||
let mut start_line_opt = None;
|
||||
let mut end_line = 0;
|
||||
for run in buffer.layout_runs() {
|
||||
end_line = run.line_i;
|
||||
if start_line_opt == None {
|
||||
start_line_opt = Some(end_line);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw scrollbar
|
||||
{
|
||||
let mut start_line_opt = None;
|
||||
let mut end_line = 0;
|
||||
for run in buffer.layout_runs() {
|
||||
end_line = run.line_i;
|
||||
if start_line_opt == None {
|
||||
start_line_opt = Some(end_line);
|
||||
}
|
||||
}
|
||||
|
||||
let start_line = start_line_opt.unwrap_or(end_line);
|
||||
let lines = buffer.text_lines().len();
|
||||
let lines = buffer.lines.len();
|
||||
let start_y = (start_line * window.height() as usize) / lines;
|
||||
let end_y = (end_line * window.height() as usize) / lines;
|
||||
if end_y > start_y {
|
||||
|
|
|
|||
|
|
@ -31,10 +31,8 @@ fn main() {
|
|||
)
|
||||
.unwrap();
|
||||
|
||||
let attrs = Attrs::new();
|
||||
let mut buffer = TextBuffer::new(
|
||||
&font_system,
|
||||
attrs,
|
||||
TextMetrics::new(32, 44).scale(display_scale)
|
||||
);
|
||||
|
||||
|
|
@ -43,6 +41,7 @@ fn main() {
|
|||
window.height() as i32
|
||||
);
|
||||
|
||||
let attrs = Attrs::new();
|
||||
let serif_attrs = attrs.family(Family::Serif);
|
||||
let mono_attrs = attrs.monospaced(true).family(Family::Monospace);
|
||||
let comic_attrs = attrs.family(Family::Name("Comic Neue"));
|
||||
|
|
@ -117,7 +116,7 @@ fn main() {
|
|||
let start = line_text.len();
|
||||
line_text.push_str(text);
|
||||
let end = line_text.len();
|
||||
attrs_list.add_span(start, end, attrs);
|
||||
attrs_list.add_span(start..end, attrs);
|
||||
}
|
||||
buffer.lines.push(TextBufferLine::new(line_text, attrs_list));
|
||||
}
|
||||
|
|
|
|||
67
src/attrs.rs
67
src/attrs.rs
|
|
@ -1,7 +1,10 @@
|
|||
// SPDX-License-Identifier: MIT OR Apache-2.0
|
||||
|
||||
use std::ops::Range;
|
||||
|
||||
pub use fontdb::{Family, Stretch, Style, Weight};
|
||||
|
||||
/// Text color
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Color(pub u32);
|
||||
|
||||
|
|
@ -48,6 +51,7 @@ impl Color {
|
|||
}
|
||||
}
|
||||
|
||||
/// Text attributes
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Attrs<'a> {
|
||||
//TODO: should this be an option?
|
||||
|
|
@ -60,6 +64,9 @@ pub struct Attrs<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Attrs<'a> {
|
||||
/// Create a new set of attributes with sane defaults
|
||||
///
|
||||
/// This defaults to a regular Sans-Serif font.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
color_opt: None,
|
||||
|
|
@ -71,36 +78,43 @@ impl<'a> Attrs<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Set [Color]
|
||||
pub fn color(mut self, color: Color) -> Self {
|
||||
self.color_opt = Some(color);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set [Family]
|
||||
pub fn family(mut self, family: Family<'a>) -> Self {
|
||||
self.family = family;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set monospaced
|
||||
pub fn monospaced(mut self, monospaced: bool) -> Self {
|
||||
self.monospaced = monospaced;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set [Stretch]
|
||||
pub fn stretch(mut self, stretch: Stretch) -> Self {
|
||||
self.stretch = stretch;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set [Style]
|
||||
pub fn style(mut self, style: Style) -> Self {
|
||||
self.style = style;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set [Weight]
|
||||
pub fn weight(mut self, weight: Weight) -> Self {
|
||||
self.weight = weight;
|
||||
self
|
||||
}
|
||||
|
||||
/// Check if font matches
|
||||
pub fn matches(&self, face: &fontdb::FaceInfo) -> bool {
|
||||
//TODO: smarter way of including emoji
|
||||
face.post_script_name.contains("Emoji") ||
|
||||
|
|
@ -112,6 +126,7 @@ impl<'a> Attrs<'a> {
|
|||
)
|
||||
}
|
||||
|
||||
/// Check if this set of attributes can be shaped with another
|
||||
pub fn compatible(&self, other: &Self) -> bool {
|
||||
self.family == other.family
|
||||
&& self.monospaced == other.monospaced
|
||||
|
|
@ -121,13 +136,15 @@ impl<'a> Attrs<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// List of text attributes to apply to a line
|
||||
#[derive(Eq, PartialEq)]
|
||||
pub struct AttrsList<'a> {
|
||||
defaults: Attrs<'a>,
|
||||
spans: Vec<(usize, usize, Attrs<'a>)>,
|
||||
spans: Vec<(Range<usize>, Attrs<'a>)>,
|
||||
}
|
||||
|
||||
impl<'a> AttrsList<'a> {
|
||||
/// Create a new attributes list with a set of default [Attrs]
|
||||
pub fn new(defaults: Attrs<'a>) -> Self {
|
||||
Self {
|
||||
defaults,
|
||||
|
|
@ -135,29 +152,65 @@ impl<'a> AttrsList<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the default [Attrs]
|
||||
pub fn defaults(&self) -> Attrs<'a> {
|
||||
self.defaults
|
||||
}
|
||||
|
||||
pub fn spans(&self) -> &Vec<(usize, usize, Attrs<'a>)> {
|
||||
/// Get the current attribute spans
|
||||
pub fn spans(&self) -> &Vec<(Range<usize>, Attrs<'a>)> {
|
||||
&self.spans
|
||||
}
|
||||
|
||||
/// Clear the current attribute spans
|
||||
pub fn clear_spans(&mut self) {
|
||||
self.spans.clear();
|
||||
}
|
||||
|
||||
pub fn add_span(&mut self, start: usize, end: usize, attrs: Attrs<'a>) {
|
||||
self.spans.push((start, end, attrs));
|
||||
/// Add an attribute span
|
||||
pub fn add_span(&mut self, range: Range<usize>, attrs: Attrs<'a>) {
|
||||
self.spans.push((range, attrs));
|
||||
}
|
||||
|
||||
pub fn get_span(&self, start: usize, end: usize) -> Attrs<'a> {
|
||||
/// Get the highest priority attribute span for a range
|
||||
///
|
||||
/// This returns the latest added span that contains the range
|
||||
pub fn get_span(&self, range: Range<usize>) -> Attrs<'a> {
|
||||
let mut attrs = self.defaults;
|
||||
for span in self.spans.iter() {
|
||||
if start >= span.0 && end <= span.1 {
|
||||
attrs = span.2;
|
||||
if range.start >= span.0.start && range.end <= span.0.end {
|
||||
attrs = span.1;
|
||||
}
|
||||
}
|
||||
attrs
|
||||
}
|
||||
|
||||
/// Split attributes list at an offset
|
||||
pub fn split_off(&mut self, index: usize) -> Self {
|
||||
let mut new = Self::new(self.defaults);
|
||||
let mut i = 0;
|
||||
while i < self.spans.len() {
|
||||
if self.spans[i].0.end <= index {
|
||||
// Leave this in the previous attributes list
|
||||
i += 1;
|
||||
} else if self.spans[i].0.start >= index {
|
||||
// Move this to the new attributes list
|
||||
let (range, attrs) = self.spans.remove(i);
|
||||
new.spans.push((
|
||||
range.start - index..range.end - index,
|
||||
attrs
|
||||
));
|
||||
} else {
|
||||
// New span has index..end
|
||||
new.spans.push((
|
||||
0..self.spans[i].0.end - index,
|
||||
self.spans[i].1
|
||||
));
|
||||
// Old span has start..index
|
||||
self.spans[i].0.end = index;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
new
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ impl fmt::Display for TextMetrics {
|
|||
}
|
||||
}
|
||||
|
||||
/// A line (or paragraph) of text that is shaped and laid out
|
||||
pub struct TextBufferLine<'a> {
|
||||
text: String,
|
||||
attrs_list: AttrsList<'a>,
|
||||
|
|
@ -182,9 +183,12 @@ pub struct TextBufferLine<'a> {
|
|||
}
|
||||
|
||||
impl<'a> TextBufferLine<'a> {
|
||||
pub fn new(text: String, attrs_list: AttrsList<'a>) -> Self {
|
||||
/// Create a new line with the given text and attributes list
|
||||
/// Cached shaping and layout can be done using the [Self::shape] and
|
||||
/// [Self::layout] functions
|
||||
pub fn new<T: Into<String>>(text: T, attrs_list: AttrsList<'a>) -> Self {
|
||||
Self {
|
||||
text,
|
||||
text: text.into(),
|
||||
attrs_list,
|
||||
wrap_simple: false,
|
||||
shape_opt: None,
|
||||
|
|
@ -198,7 +202,8 @@ impl<'a> TextBufferLine<'a> {
|
|||
}
|
||||
|
||||
/// Set text
|
||||
/// Will reset shape and layout if it differs from current text
|
||||
///
|
||||
/// Will reset shape and layout if it differs from current text.
|
||||
/// Returns true if the line was reset
|
||||
pub fn set_text<T: AsRef<str> + Into<String>>(&mut self, text: T) -> bool {
|
||||
if text.as_ref() != &self.text {
|
||||
|
|
@ -215,8 +220,9 @@ impl<'a> TextBufferLine<'a> {
|
|||
&self.attrs_list
|
||||
}
|
||||
|
||||
/// Set attributes list.
|
||||
/// Will reset shape and layout if it differs from current attributes list
|
||||
/// Set attributes list
|
||||
///
|
||||
/// Will reset shape and layout if it differs from current attributes list.
|
||||
/// Returns true if the line was reset
|
||||
pub fn set_attrs_list(&mut self, attrs_list: AttrsList<'a>) -> bool {
|
||||
if attrs_list != self.attrs_list {
|
||||
|
|
@ -234,7 +240,8 @@ impl<'a> TextBufferLine<'a> {
|
|||
}
|
||||
|
||||
/// Set simple wrapping setting (wrap by characters only)
|
||||
/// Will reset shape and layout if it differs from current simple wrapping setting
|
||||
///
|
||||
/// Will reset shape and layout if it differs from current simple wrapping setting.
|
||||
/// Returns true if the line was reset
|
||||
pub fn set_wrap_simple(&mut self, wrap_simple: bool) -> bool {
|
||||
if wrap_simple != self.wrap_simple {
|
||||
|
|
@ -288,7 +295,7 @@ impl<'a> TextBufferLine<'a> {
|
|||
/// A buffer of text that is shaped and laid out
|
||||
pub struct TextBuffer<'a> {
|
||||
font_system: &'a FontSystem<'a>,
|
||||
attrs: Attrs<'a>,
|
||||
/// Lines (or paragraphs) of text in the buffer
|
||||
pub lines: Vec<TextBufferLine<'a>>,
|
||||
metrics: TextMetrics,
|
||||
width: i32,
|
||||
|
|
@ -297,19 +304,22 @@ pub struct TextBuffer<'a> {
|
|||
cursor: TextCursor,
|
||||
cursor_x_opt: Option<i32>,
|
||||
select_opt: Option<TextCursor>,
|
||||
/// True if the cursor has been moved. Set to false after processing
|
||||
///
|
||||
/// Usually, if this is true, you should run [Self::shape_until_cursor] before redrawing.
|
||||
/// Otherwise, you should run [Self::shape_until_scroll]
|
||||
pub cursor_moved: bool,
|
||||
/// True if a redraw is requires. Set to false after processing
|
||||
pub redraw: bool,
|
||||
}
|
||||
|
||||
impl<'a> TextBuffer<'a> {
|
||||
pub fn new(
|
||||
font_system: &'a FontSystem<'a>,
|
||||
attrs: Attrs<'a>,
|
||||
metrics: TextMetrics,
|
||||
) -> Self {
|
||||
let mut buffer = Self {
|
||||
font_system,
|
||||
attrs,
|
||||
lines: Vec::new(),
|
||||
metrics,
|
||||
width: 0,
|
||||
|
|
@ -321,7 +331,7 @@ impl<'a> TextBuffer<'a> {
|
|||
cursor_moved: false,
|
||||
redraw: false,
|
||||
};
|
||||
buffer.set_text("");
|
||||
buffer.set_text("", Attrs::new());
|
||||
buffer
|
||||
}
|
||||
|
||||
|
|
@ -400,6 +410,7 @@ impl<'a> TextBuffer<'a> {
|
|||
self.shape_until_scroll();
|
||||
}
|
||||
|
||||
/// Shape lines until scroll
|
||||
pub fn shape_until_scroll(&mut self) {
|
||||
let lines = self.lines();
|
||||
|
||||
|
|
@ -558,33 +569,15 @@ impl<'a> TextBuffer<'a> {
|
|||
self.height / self.metrics.line_height
|
||||
}
|
||||
|
||||
pub fn attrs(&self) -> &Attrs<'a> {
|
||||
&self.attrs
|
||||
}
|
||||
|
||||
/// Set attributes
|
||||
pub fn set_attrs(&mut self, attrs: Attrs<'a>) {
|
||||
if attrs != self.attrs {
|
||||
self.attrs = attrs;
|
||||
|
||||
for line in self.lines.iter_mut() {
|
||||
line.attrs_list = AttrsList::new(attrs);
|
||||
line.reset();
|
||||
}
|
||||
|
||||
self.shape_until_scroll();
|
||||
}
|
||||
}
|
||||
|
||||
/// Set text of buffer
|
||||
pub fn set_text(&mut self, text: &str) {
|
||||
/// Set text of buffer, using provided attributes for each line by default
|
||||
pub fn set_text(&mut self, text: &str, attrs: Attrs<'a>) {
|
||||
self.lines.clear();
|
||||
for line in text.lines() {
|
||||
self.lines.push(TextBufferLine::new(line.to_string(), AttrsList::new(self.attrs)));
|
||||
self.lines.push(TextBufferLine::new(line.to_string(), AttrsList::new(attrs)));
|
||||
}
|
||||
// Make sure there is always one line
|
||||
if self.lines.is_empty() {
|
||||
self.lines.push(TextBufferLine::new(String::new(), AttrsList::new(self.attrs)));
|
||||
self.lines.push(TextBufferLine::new(String::new(), AttrsList::new(attrs)));
|
||||
}
|
||||
|
||||
self.scroll = 0;
|
||||
|
|
@ -594,11 +587,6 @@ impl<'a> TextBuffer<'a> {
|
|||
self.shape_until_scroll();
|
||||
}
|
||||
|
||||
/// Get the lines of the original text
|
||||
pub fn text_lines(&self) -> &[TextBufferLine] {
|
||||
&self.lines
|
||||
}
|
||||
|
||||
/// Perform a [TextAction] on the buffer
|
||||
pub fn action(&mut self, action: TextAction) {
|
||||
let old_cursor = self.cursor;
|
||||
|
|
@ -763,14 +751,16 @@ impl<'a> TextBuffer<'a> {
|
|||
let new_line = {
|
||||
let line = &mut self.lines[self.cursor.line];
|
||||
line.reset();
|
||||
line.text.split_off(self.cursor.index)
|
||||
TextBufferLine::new(
|
||||
line.text.split_off(self.cursor.index),
|
||||
line.attrs_list.split_off(self.cursor.index)
|
||||
)
|
||||
};
|
||||
|
||||
let next_line = self.cursor.line + 1;
|
||||
self.lines.insert(next_line, TextBufferLine::new(new_line, AttrsList::new(self.attrs)));
|
||||
|
||||
self.cursor.line = next_line;
|
||||
self.cursor.line += 1;
|
||||
self.cursor.index = 0;
|
||||
|
||||
self.lines.insert(self.cursor.line, new_line);
|
||||
},
|
||||
TextAction::Backspace => {
|
||||
if self.cursor.index > 0 {
|
||||
|
|
|
|||
10
src/shape.rs
10
src/shape.rs
|
|
@ -94,7 +94,7 @@ fn shape_fallback(
|
|||
// Set color
|
||||
//TODO: these attributes should not be related to shaping
|
||||
for glyph in glyphs.iter_mut() {
|
||||
let attrs = attrs_list.get_span(glyph.start, glyph.end);
|
||||
let attrs = attrs_list.get_span(glyph.start..glyph.end);
|
||||
glyph.color_opt = attrs.color_opt;
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +129,7 @@ fn shape_run<'a>(
|
|||
&line[start_run..end_run],
|
||||
);
|
||||
|
||||
let attrs = attrs_list.get_span(start_run, end_run);
|
||||
let attrs = attrs_list.get_span(start_run..end_run);
|
||||
|
||||
let font_matches = font_system.get_font_matches(attrs);
|
||||
|
||||
|
|
@ -234,6 +234,7 @@ fn shape_run<'a>(
|
|||
glyphs
|
||||
}
|
||||
|
||||
/// A shaped glyph
|
||||
pub struct ShapeGlyph {
|
||||
pub start: usize,
|
||||
pub end: usize,
|
||||
|
|
@ -272,6 +273,7 @@ impl ShapeGlyph {
|
|||
}
|
||||
}
|
||||
|
||||
/// A shaped word (for word wrapping)
|
||||
pub struct ShapeWord {
|
||||
pub blank: bool,
|
||||
pub glyphs: Vec<ShapeGlyph>,
|
||||
|
|
@ -302,7 +304,7 @@ impl ShapeWord {
|
|||
for (egc_i, egc) in word.grapheme_indices(true) {
|
||||
let start_egc = start_word + egc_i;
|
||||
let end_egc = start_egc + egc.len();
|
||||
let attrs_egc = attrs_list.get_span(start_egc, end_egc);
|
||||
let attrs_egc = attrs_list.get_span(start_egc..end_egc);
|
||||
if ! attrs.compatible(&attrs_egc) {
|
||||
//TODO: more efficient
|
||||
glyphs.append(&mut shape_run(
|
||||
|
|
@ -334,6 +336,7 @@ impl ShapeWord {
|
|||
}
|
||||
}
|
||||
|
||||
/// A shaped span (for bidirectional processing)
|
||||
pub struct ShapeSpan {
|
||||
pub rtl: bool,
|
||||
pub words: Vec<ShapeWord>,
|
||||
|
|
@ -413,6 +416,7 @@ impl ShapeSpan {
|
|||
}
|
||||
}
|
||||
|
||||
/// A shaped line (or paragraph)
|
||||
pub struct ShapeLine {
|
||||
pub rtl: bool,
|
||||
pub spans: Vec<ShapeSpan>,
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ fn swash_image<'a>(font_system: &'a FontSystem<'a>, context: &mut ScaleContext,
|
|||
.render(&mut scaler, cache_key.glyph_id)
|
||||
}
|
||||
|
||||
/// Cache for rasterizing with the swash scaler
|
||||
pub struct SwashCache<'a> {
|
||||
font_system: &'a FontSystem<'a>,
|
||||
context: ScaleContext,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue