mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-09 19:00:46 +00:00
Implement From<Effect> and From<ColorStyle> for Style
This commit is contained in:
parent
67e230e246
commit
1acde148be
44
src/theme.rs
44
src/theme.rs
@ -120,6 +120,50 @@ use std::io::Read;
|
||||
use std::path::Path;
|
||||
use toml;
|
||||
|
||||
/// Combine a color and an effect.
|
||||
///
|
||||
/// Represents any transformation that can be applied to text.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Style {
|
||||
/// Effect to apply.
|
||||
///
|
||||
/// `None` to keep using previous effects.
|
||||
pub effect: Option<Effect>,
|
||||
|
||||
/// Color style to apply.
|
||||
///
|
||||
/// `None` to keep using the previous colors.
|
||||
pub color: Option<ColorStyle>,
|
||||
}
|
||||
|
||||
impl Style {
|
||||
/// Returns a new `Style` that doesn't apply anything.
|
||||
pub fn none() -> Self {
|
||||
Style {
|
||||
effect: None,
|
||||
color: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Effect> for Style {
|
||||
fn from(effect: Effect) -> Self {
|
||||
Style {
|
||||
effect: Some(effect),
|
||||
color: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColorStyle> for Style {
|
||||
fn from(color: ColorStyle) -> Self {
|
||||
Style {
|
||||
effect: None,
|
||||
color: Some(color),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Text effect
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Effect {
|
||||
|
150
src/utils/span_lines_iterator.rs
Normal file
150
src/utils/span_lines_iterator.rs
Normal file
@ -0,0 +1,150 @@
|
||||
use std::borrow::Cow;
|
||||
use theme::Style;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
use xi_unicode::LineBreakLeafIter;
|
||||
|
||||
pub struct Span<'a> {
|
||||
text: Cow<'a, str>,
|
||||
width: usize,
|
||||
style: Style,
|
||||
}
|
||||
|
||||
pub struct Row<'a> {
|
||||
spans: Vec<Span<'a>>,
|
||||
width: usize,
|
||||
}
|
||||
|
||||
pub struct SpanLinesIterator<'a: 'b, 'b> {
|
||||
/// Input that we want to split
|
||||
content: &'b [Span<'a>],
|
||||
|
||||
/// Available width
|
||||
width: usize,
|
||||
|
||||
current_span: usize,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a: 'b, 'b> SpanLinesIterator<'a, 'b> {
|
||||
pub fn new(content: &'b [Span<'a>], width: usize) -> Self {
|
||||
SpanLinesIterator {
|
||||
content,
|
||||
width,
|
||||
current_span: 0,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Intermediate representation of a Span, easier to manipulate.
|
||||
struct Segment {
|
||||
span_id: usize,
|
||||
start: usize,
|
||||
end: usize,
|
||||
width: usize,
|
||||
}
|
||||
|
||||
impl<'a, 'b> Iterator for SpanLinesIterator<'a, 'b> {
|
||||
type Item = Row<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Row<'a>> {
|
||||
if self.current_span >= self.content.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let current_span = &self.content[self.current_span];
|
||||
|
||||
let mut available = self.width;
|
||||
let mut iter = LineBreakLeafIter::new(¤t_span.text, self.offset);
|
||||
|
||||
let mut spans = Vec::new();
|
||||
let mut width = 0;
|
||||
|
||||
// We'll build a list of segments.
|
||||
// There will be a 1-for-1 mapping from segments to spans.
|
||||
// But segments are easier to manipulate and extend for now.
|
||||
let mut segments: Vec<Segment> = Vec::new();
|
||||
|
||||
// When a span does not end on a possible break, its last segment
|
||||
// can only be included depending on what comes after.
|
||||
// So we keep a list of consecutive segments ids without breaks.
|
||||
let mut carry_over: Vec<usize> = Vec::new();
|
||||
// Whenever a segment is accepted, all of these can be inserted too.
|
||||
|
||||
'outer: for (span_id, span) in
|
||||
self.content.iter().enumerate().skip(self.current_span)
|
||||
{
|
||||
// Make a new segment!
|
||||
loop {
|
||||
// Get the next possible break point.
|
||||
let (pos, hard) = iter.next(&span.text);
|
||||
|
||||
// Lookup the corresponding text segment.
|
||||
let segment = &span.text[self.offset..pos];
|
||||
let width = segment.width();
|
||||
|
||||
// If it doesn't fit, it's time to go home.
|
||||
if width > available {
|
||||
// Early return!
|
||||
break 'outer;
|
||||
}
|
||||
|
||||
available -= width;
|
||||
|
||||
// It fits, but... for real?
|
||||
if pos == span.text.len() {
|
||||
// It was too good to be true!
|
||||
// It's just the end of a span, not an actual break.
|
||||
// So save this stub for now, and move on to the next span.
|
||||
carry_over.push(span_id);
|
||||
// Start on the next span.
|
||||
self.offset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// We got it! We got a chunk!
|
||||
// First, append any carry-over segment
|
||||
for carry in carry_over.drain(..) {
|
||||
// We need to include this entire segment.
|
||||
if segments.last().map(|s| s.span_id) == Some(carry) {
|
||||
|
||||
} else {
|
||||
segments.push(Segment {});
|
||||
}
|
||||
}
|
||||
|
||||
// Include the present segment.
|
||||
if pos != 0 {
|
||||
segments.push(Segment {
|
||||
span_id,
|
||||
width,
|
||||
start: self.offset,
|
||||
end: pos,
|
||||
});
|
||||
|
||||
self.offset = pos;
|
||||
}
|
||||
|
||||
if hard {
|
||||
// Stop here.
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
let current_span = &self.content[self.current_span];
|
||||
let (pos, hard) = iter.next(¤t_span.text);
|
||||
|
||||
// This is what we consider adding
|
||||
let text = ¤t_span.text[self.offset..pos];
|
||||
|
||||
if hard {
|
||||
// Stop there!
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Some(Row { spans, width })
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user