From 6b278495d54e5b4a3d983d94fa546ff9aa4ca916 Mon Sep 17 00:00:00 2001 From: Alexandre Bury Date: Fri, 30 Nov 2018 13:19:08 -0800 Subject: [PATCH] Use StyledString in SelectView --- src/printer.rs | 17 ++++++++++++++++- src/utils/span.rs | 28 +++++++++++++++++----------- src/views/select_view.rs | 38 ++++++++++++++++++-------------------- 3 files changed, 51 insertions(+), 32 deletions(-) diff --git a/src/printer.rs b/src/printer.rs index 03022ac..00d1403 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -84,9 +84,24 @@ impl<'a, 'b> Printer<'a, 'b> { .clear(self.theme.palette[PaletteColor::Background]); } + /// Prints some styled text at the given position. + pub fn print_styled( + &self, start: S, text: ::utils::span::SpannedStr<'_, Style>, + ) where + S: Into, + { + let Vec2 { mut x, y } = start.into(); + for span in text.spans() { + self.with_style(*span.attr, |printer| { + printer.print((x, y), span.content); + x += span.content.width(); + }); + } + } + // TODO: use &mut self? We don't *need* it, but it may make sense. // We don't want people to start calling prints in parallel? - /// Prints some text at the given position relative to the window. + /// Prints some text at the given position pub fn print>(&self, start: S, text: &str) { // Where we are asked to start printing. Oh boy. let start = start.into(); diff --git a/src/utils/span.rs b/src/utils/span.rs index bbc11b1..1a0dc2f 100644 --- a/src/utils/span.rs +++ b/src/utils/span.rs @@ -3,6 +3,7 @@ //! This module defines various structs describing a span of text from a //! larger string. use std::borrow::Cow; +use unicode_width::UnicodeWidthStr; /// A string with associated spans. /// @@ -117,11 +118,12 @@ where } /// Gives access to the parsed styled spans. - pub fn spans(&self) -> Vec> { - self.spans - .iter() - .map(|span| span.resolve(self.source)) - .collect() + pub fn spans<'b>(&'b self) -> impl Iterator> + 'b + where + 'a: 'b, + { + let source = self.source; + self.spans.iter().map(move |span| span.resolve(source)) } /// Returns a reference to the indexed spans. @@ -232,12 +234,9 @@ impl SpannedString { } /// Gives access to the parsed styled spans. - #[cfg_attr(feature = "cargo-clippy", allow(needless_lifetimes))] - pub fn spans<'a>(&'a self) -> Vec> { - self.spans - .iter() - .map(|span| span.resolve(&self.source)) - .collect() + pub fn spans<'a>(&'a self) -> impl Iterator> { + let source = &self.source; + self.spans.iter().map(move |span| span.resolve(source)) } /// Returns a reference to the indexed spans. @@ -256,6 +255,13 @@ impl SpannedString { pub fn is_empty(&self) -> bool { self.source.is_empty() || self.spans.is_empty() } + + /// Returns the width taken by this string. + /// + /// This is the sum of the width of each span. + pub fn width(&self) -> usize { + self.spans().map(|s| s.content.width()).sum() + } } impl<'a, T> From<&'a SpannedString> for SpannedStr<'a, T> { diff --git a/src/views/select_view.rs b/src/views/select_view.rs index 7a8e7d3..264a38c 100644 --- a/src/views/select_view.rs +++ b/src/views/select_view.rs @@ -8,7 +8,7 @@ use std::cell::Cell; use std::cmp::min; use std::rc::Rc; use theme::ColorStyle; -use unicode_width::UnicodeWidthStr; +use utils::markup::StyledString; use vec::Vec2; use view::{Position, View}; use views::MenuPopup; @@ -253,7 +253,7 @@ impl SelectView { } /// Adds a item to the list, with given label and value. - pub fn add_item>(&mut self, label: S, value: T) { + pub fn add_item>(&mut self, label: S, value: T) { self.items.push(Item::new(label.into(), value)); } @@ -267,13 +267,13 @@ impl SelectView { /// assert_eq!(select.get_item(0), Some(("Short", &1))); /// ``` pub fn get_item(&self, i: usize) -> Option<(&str, &T)> { - self.items - .get(i) - .map(|item| (item.label.as_ref(), &*item.value)) + self.iter().nth(i) } /// Gets a mut item at given idx or None. - pub fn get_item_mut(&mut self, i: usize) -> Option<(&mut String, &mut T)> { + pub fn get_item_mut( + &mut self, i: usize, + ) -> Option<(&mut StyledString, &mut T)> { if i >= self.items.len() { None } else { @@ -293,7 +293,7 @@ impl SelectView { pub fn iter(&self) -> impl Iterator { self.items .iter() - .map(|item| (item.label.as_str(), &*item.value)) + .map(|item| (item.label.source(), &*item.value)) } /// Removes an item from the list. @@ -315,7 +315,7 @@ impl SelectView { /// the right. pub fn insert_item(&mut self, index: usize, label: S, value: T) where - S: Into, + S: Into, { self.items.insert(index, Item::new(label.into(), value)); } @@ -328,7 +328,7 @@ impl SelectView { /// Adds all items from from an iterator. pub fn add_all(&mut self, iter: I) where - S: Into, + S: Into, I: IntoIterator, { for (s, t) in iter { @@ -351,7 +351,7 @@ impl SelectView { let l = self.items[i].label.width(); let x = self.align.h.get_offset(l, printer.size.x); printer.print_hline((0, 0), x, " "); - printer.print((x, 0), &self.items[i].label); + printer.print_styled((x, 0), (&self.items[i].label).into()); if l < printer.size.x { assert!((l + x) <= printer.size.x); printer.print_hline((x + l, 0), printer.size.x - (l + x), " "); @@ -554,7 +554,7 @@ impl SelectView { let focus = Rc::clone(&self.focus); let on_submit = self.on_submit.as_ref().cloned(); let value = Rc::clone(&item.value); - tree.add_leaf(item.label.clone(), move |s| { + tree.add_leaf(item.label.source(), move |s| { // TODO: What if an item was removed in the meantime? focus.set(i); if let Some(ref on_submit) = on_submit { @@ -570,7 +570,7 @@ impl SelectView { // This is the offset for the label text. // We'll want to show the popup so that the text matches. // It'll be soo cool. - let item_length = self.items[focus].label.len(); + let item_length = self.items[focus].label.width(); let text_offset = (self.last_size.x.saturating_sub(item_length)) / 2; // The total offset for the window is: // * the last absolute offset at which we drew this view @@ -698,9 +698,9 @@ impl View for SelectView { let label = &self.items[self.focus()].label; // And center the text? - let offset = HAlign::Center.get_offset(label.len(), x + 1); + let offset = HAlign::Center.get_offset(label.width(), x + 1); - printer.print((offset, 0), label); + printer.print_styled((offset, 0), label.into()); }); } else { // Non-popup mode: we always print the entire list. @@ -772,15 +772,13 @@ impl View for SelectView { // We wrap each value in a `Rc` and add a label struct Item { - label: String, + label: StyledString, value: Rc, } impl Item { - fn new(label: String, value: T) -> Self { - Item { - label, - value: Rc::new(value), - } + fn new(label: StyledString, value: T) -> Self { + let value = Rc::new(value); + Item { label, value } } }