use super::tui::OurKey;
use super::coloured_string::{ColouredString, ColouredStringSlice};
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum HighlightType { User, Status, WholeStatus }
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct Highlight(pub HighlightType, pub usize);
+
+trait ConsumableHighlight {
+ fn consume(&mut self, htype: HighlightType, n: usize) -> Option<usize>;
+}
+
+impl ConsumableHighlight for Option<Highlight> {
+ fn consume(&mut self, htype: HighlightType, n: usize) -> Option<usize> {
+ let (answer, new_self) = match *self {
+ Some(hl) => {
+ if hl.0 != htype {
+ (None, Some(hl))
+ } else if hl.1 < n {
+ (Some(hl.1), None)
+ } else {
+ (None, Some(Highlight(hl.0, hl.1 - n)))
+ }
+ }
+ None => (None, None),
+ };
+ *self = new_self;
+ answer
+ }
+}
+
pub trait TextFragment {
- // FIXME: we will also need ...
- // Some indication of how many usernames are in here and how many statuses
- // Some means of passing a flag to render() that says which username or
- // status is to be highlighted
- fn render(&self, width: usize) -> Vec<ColouredString>;
+ fn render(&self, width: usize) -> Vec<ColouredString> {
+ self.render_highlighted(width, None)
+ }
+
+ fn can_highlight(_htype: HighlightType) -> bool where Self : Sized {
+ false
+ }
+
+ fn count_highlightables(&self, _htype: HighlightType) -> usize { 0 }
+ fn highlighted_id(&self, _highlight: Option<Highlight>) -> Option<String> {
+ None
+ }
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>;
+
+ fn render_highlighted_update(
+ &self, width: usize, highlight: &mut Option<Highlight>)
+ -> Vec<ColouredString>
+ {
+ let (new_highlight, text) = match *highlight {
+ Some(Highlight(htype, index)) => {
+ let count = self.count_highlightables(htype);
+ if index < count {
+ (None, self.render_highlighted(width, *highlight))
+ } else {
+ (Some(Highlight(htype, index - count)),
+ self.render_highlighted(width, None))
+ }
+ }
+ None => {
+ (None, self.render_highlighted(width, None))
+ }
+ };
+ *highlight = new_highlight;
+ text
+ }
+
+ fn highlighted_id_update(
+ &self, highlight: &mut Option<Highlight>) -> Option<String>
+ {
+ let (answer, new_highlight) = match *highlight {
+ Some(Highlight(htype, index)) => {
+ let count = self.count_highlightables(htype);
+ if index < count {
+ (self.highlighted_id(Some(Highlight(htype, index))),
+ None)
+ } else {
+ (None, Some(Highlight(htype, index - count)))
+ }
+ }
+ None => (None, None),
+ };
+ *highlight = new_highlight;
+ answer
+ }
}
impl<T: TextFragment> TextFragment for Option<T> {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn can_highlight(htype: HighlightType) -> bool where Self : Sized {
+ T::can_highlight(htype)
+ }
+
+ fn count_highlightables(&self, htype: HighlightType) -> usize {
match self {
- Some(ref inner) => inner.render(width),
+ Some(ref inner) => inner.count_highlightables(htype),
+ None => 0,
+ }
+ }
+ fn render_highlighted(&self, width: usize, highlight: Option<Highlight>)
+ -> Vec<ColouredString> {
+ match self {
+ Some(ref inner) => inner.render_highlighted(width, highlight),
None => Vec::new(),
}
}
+ fn highlighted_id(&self, highlight: Option<Highlight>) -> Option<String> {
+ match self {
+ Some(ref inner) => inner.highlighted_id(highlight),
+ None => None,
+ }
+ }
}
impl<T: TextFragment> TextFragment for Vec<T> {
- fn render(&self, width: usize) -> Vec<ColouredString> {
- itertools::concat(self.iter().map(|x| x.render(width)))
+ fn can_highlight(htype: HighlightType) -> bool where Self : Sized {
+ T::can_highlight(htype)
+ }
+
+ fn count_highlightables(&self, htype: HighlightType) -> usize {
+ self.iter().map(|x| x.count_highlightables(htype)).sum()
+ }
+ fn render_highlighted(&self, width: usize, highlight: Option<Highlight>)
+ -> Vec<ColouredString> {
+ let mut highlight = highlight;
+ itertools::concat(self.iter().map(
+ |x| x.render_highlighted_update(width, &mut highlight)))
+ }
+ fn highlighted_id(&self, highlight: Option<Highlight>) -> Option<String> {
+ let mut highlight = highlight;
+ for item in self {
+ match item.highlighted_id_update(&mut highlight) {
+ result @ Some(..) => return result,
+ _ => (),
+ }
+ }
+ None
}
}
}
impl TextFragment for BlankLine {
- fn render(&self, _width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, _width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
Self::render_static()
}
}
}
impl TextFragment for SeparatorLine {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut suffix = ColouredString::plain("");
let display_pre = ColouredString::uniform("[", 'S');
let display_post = ColouredString::uniform("]--", 'S');
}
impl TextFragment for EditorHeaderSeparator {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
vec! {
ColouredString::uniform(
&((&"-".repeat(width - min(2, width))).to_owned() + "|"),
}
impl TextFragment for UsernameHeader {
- fn render(&self, _width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, _width: usize, highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let header = ColouredString::plain(&format!("{}: ", self.header));
let body = ColouredString::uniform(
&format!("{} ({})", self.nameline, self.account), self.colour);
}
impl TextFragment for Paragraph {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut lines = Vec::new();
let mut curr_width = 0;
let mut curr_pos;
}
impl TextFragment for FileHeader {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let elephants = width >= 16;
let twidth = if elephants { width - 9 } else { width - 1 };
let title = self.text.truncate(twidth);
}
impl TextFragment for Html {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
match self {
Html::Rt(ref rt) => html::render(rt, width - min(width, 1)),
Html::Bad(e) => vec! {
}
impl TextFragment for ExtendableIndicator {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
// FIXME: decide how to make this message change when it's primed
let message = if self.primed {
ColouredString::general(
}
impl TextFragment for InReplyToLine {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let rendered_para = self.para.render(width - min(width, 3));
let mut it = rendered_para.iter();
// "Re:" guarantees the first line must exist at least
}
impl TextFragment for NotificationLog {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut full_para = Paragraph::new().set_indent(0, 2);
let datestr = format_date(self.timestamp);
full_para.push_text(&ColouredString::uniform(&datestr, ' '), false);
}
impl TextFragment for UserListEntry {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut para = Paragraph::new().set_indent(0, 2);
// FIXME: highlight account_desc if active
para.push_text(
}
impl TextFragment for Media {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut lines = vec! { ColouredString::uniform(&self.url, 'M') };
for para in &self.description {
lines.extend_from_slice(¶.render(width));
}
impl TextFragment for FileStatusLineFinal {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut line = ColouredString::plain("");
let space = ColouredString::plain(" ").repeat(FileStatusLine::SPACING);
let push = |line: &mut ColouredString, s: ColouredStringSlice<'_>| {
}
impl TextFragment for MenuKeypressLine {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let ourwidth = self.lmaxwid + self.rmaxwid + 3; // " = " in the middle
let space = width - min(width, ourwidth + 1);
let leftpad = min(space * 3 / 4, width - min(width, self.lmaxwid + 2));
}
impl TextFragment for StatusDisplay {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut lines = Vec::new();
push_fragment(&mut lines, self.sep.render(width));
}
impl TextFragment for DetailedStatusDisplay {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut lines = Vec::new();
push_fragment(&mut lines, self.sd.render(width));
}
impl TextFragment for ExamineUserDisplay {
- fn render(&self, width: usize) -> Vec<ColouredString> {
+ fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>)
+ -> Vec<ColouredString>
+ {
let mut lines = Vec::new();
push_fragment(&mut lines, self.name.render(width));