#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum SearchDirection { Up, Down }
+#[derive(Default)]
+struct FileDisplayStyles {
+ selected_poll_id: Option<String>,
+ selected_poll_options: HashSet<usize>,
+}
+impl DisplayStyleGetter for FileDisplayStyles {
+ fn poll_options(&self, id: &str) -> Option<HashSet<usize>> {
+ self.selected_poll_id.as_ref().and_then(
+ |s| if s == id {
+ Some(self.selected_poll_options.clone())
+ } else {
+ None
+ }
+ )
+ }
+}
+
struct File<Type: FileType, Source: FileDataSource> {
contents: FileContents<Type, Source>,
rendered: HashMap<isize, Vec<ColouredString>>,
selection: Option<(isize, usize)>,
selection_restrict_to_item: Option<isize>,
select_aux: Option<bool>, // distinguishes fave from unfave, etc
- poll_options_selected: HashSet<usize>,
+ display_styles: FileDisplayStyles,
file_desc: Type,
search_direction: Option<SearchDirection>,
last_search: Option<Regex>,
selection: None,
selection_restrict_to_item: None,
select_aux: None,
- poll_options_selected: HashSet::new(),
+ display_styles: FileDisplayStyles::default(),
file_desc,
search_direction: None,
last_search: None,
_ => None,
};
- let poll_options =
- if self.selection_restrict_to_item == Some(index) {
- Some(&self.poll_options_selected)
- } else {
- None
- };
-
for line in self.contents.get(index)
- .render_highlighted(w, highlight, poll_options)
+ .render_highlighted(w, highlight, &self.display_styles)
{
for frag in line.split(w) {
lines.push(frag.into());
LogicalAction::Beep
};
+ self.display_styles.selected_poll_id = match self.ui_mode {
+ UIMode::Select(HighlightType::PollOption, _) =>
+ self.selected_id(self.selection),
+ _ => None,
+ };
+
self.select_aux = match self.ui_mode {
UIMode::Select(_, SelectionPurpose::Favourite) => {
match self.selected_id(self.selection) {
"we should only call this if we have a selection");
self.selection_restrict_to_item = Some(item);
if self.contents.get(item).is_multiple_choice_poll() {
- if self.poll_options_selected.contains(&sub) {
- self.poll_options_selected.remove(&sub);
+ if self.display_styles.selected_poll_options.contains(&sub) {
+ self.display_styles.selected_poll_options.remove(&sub);
} else {
- self.poll_options_selected.insert(sub);
+ self.display_styles.selected_poll_options.insert(sub);
}
} else {
- self.poll_options_selected.clear();
- self.poll_options_selected.insert(sub);
+ self.display_styles.selected_poll_options.clear();
+ self.display_styles.selected_poll_options.insert(sub);
}
- dbg!(&self.poll_options_selected);
self.rerender_selected_item();
self.ensure_enough_rendered();
LogicalAction::Nothing
self.selection = None;
self.ensure_enough_rendered();
}
- self.poll_options_selected.clear();
+ self.display_styles.selected_poll_id = None;
+ self.display_styles.selected_poll_options.clear();
self.ui_mode = UIMode::Normal;
}
UtilityActivity::ThreadFile(id, alt).into()),
SelectionPurpose::Vote => {
match client.vote_in_poll(
- &id, self.poll_options_selected.iter().copied()) {
+ &id, self.display_styles.selected_poll_options.iter().copied()) {
Ok(_) => {
self.contents.update_items(client);
LogicalAction::Nothing
// the keypress for selecting something.
// Otherwise, prioritise the one for
// submitting your answer.
- if self.poll_options_selected.is_empty() {
+ if self.display_styles.selected_poll_options
+ .is_empty()
+ {
fs.add(Space, verb, 98)
.add(Ctrl('V'), "Submit Vote", 97)
} else {
}
}
+// Trait that can be passed by the caller of render_highlighted(),
+// providing information to modify how things are displayed
+pub trait DisplayStyleGetter {
+ fn poll_options(&self, id: &str) -> Option<HashSet<usize>>;
+}
+struct DefaultDisplayStyle;
+impl DisplayStyleGetter for DefaultDisplayStyle {
+ fn poll_options(&self, _id: &str) -> Option<HashSet<usize>> { None }
+}
+
pub trait TextFragment {
fn render(&self, width: usize) -> Vec<ColouredString> {
- self.render_highlighted(width, None, None)
+ self.render_highlighted(width, None, &DefaultDisplayStyle)
}
fn can_highlight(_htype: HighlightType) -> bool where Self : Sized {
None
}
fn render_highlighted(&self, width: usize, highlight: Option<Highlight>,
- poll_options: Option<&HashSet<usize>>)
+ style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>;
fn is_multiple_choice_poll(&self) -> bool { false }
fn render_highlighted_update(
&self, width: usize, highlight: &mut Option<Highlight>,
- poll_options: Option<&HashSet<usize>>)
- -> Vec<ColouredString>
+ style: &dyn DisplayStyleGetter) -> 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, poll_options))
+ (None, self.render_highlighted(width, *highlight, style))
} else {
(Some(Highlight(htype, index - count)),
- self.render_highlighted(width, None, poll_options))
+ self.render_highlighted(width, None, style))
}
}
None => {
- (None, self.render_highlighted(width, None, poll_options))
+ (None, self.render_highlighted(width, None, style))
}
};
*highlight = new_highlight;
}
}
fn render_highlighted(&self, width: usize, highlight: Option<Highlight>,
- poll_options: Option<&HashSet<usize>>)
+ style: &dyn DisplayStyleGetter)
-> Vec<ColouredString> {
match self {
Some(ref inner) => inner.render_highlighted(
- width, highlight, poll_options),
+ width, highlight, style),
None => Vec::new(),
}
}
self.iter().map(|x| x.count_highlightables(htype)).sum()
}
fn render_highlighted(&self, width: usize, highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ style: &dyn DisplayStyleGetter)
-> Vec<ColouredString> {
let mut highlight = highlight;
- // Ignore poll_options, because that's only used when we're
- // rendering a single item containing the poll in question
itertools::concat(self.iter().map(
- |x| x.render_highlighted_update(width, &mut highlight, None)))
+ |x| x.render_highlighted_update(width, &mut highlight, style)))
}
fn highlighted_id(&self, highlight: Option<Highlight>) -> Option<String> {
let mut highlight = highlight;
impl TextFragment for BlankLine {
fn render_highlighted(&self, _width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
Self::render_static()
impl TextFragment for SeparatorLine {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut suffix = ColouredString::plain("");
impl TextFragment for EditorHeaderSeparator {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
vec! {
impl TextFragment for UsernameHeader {
fn render_highlighted(&self, _width: usize, highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let header = ColouredString::plain(&format!("{}: ", self.header));
impl TextFragment for Paragraph {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut lines = Vec::new();
impl TextFragment for FileHeader {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let elephants = width >= 16;
impl TextFragment for Html {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
match self {
impl TextFragment for ExtendableIndicator {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let message = if self.primed {
impl TextFragment for InReplyToLine {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let rendered_para = self.para.render(width - min(width, 3));
impl TextFragment for VisibilityLine {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let line = match self.vis {
impl TextFragment for NotificationLog {
fn render_highlighted(&self, width: usize, highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut full_para = Paragraph::new().set_indent(0, 2);
impl TextFragment for UserListEntry {
fn render_highlighted(&self, width: usize, highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut para = Paragraph::new().set_indent(0, 2);
impl TextFragment for Media {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut lines: Vec<_> = ColouredString::uniform(&self.url, 'M')
impl TextFragment for FileStatusLineFinal {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut line = ColouredString::plain("");
impl TextFragment for MenuKeypressLine {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let ourwidth = self.lmaxwid + self.rmaxwid + 3; // " = " in the middle
impl TextFragment for CentredInfoLine {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let twidth = width.saturating_sub(1);
impl TextFragment for StatusDisplay {
fn render_highlighted(&self, width: usize, highlight: Option<Highlight>,
- poll_options_selected: Option<&HashSet<usize>>)
+ style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut lines = Vec::new();
push_fragment(&mut lines, self.sep.render(width));
push_fragment(&mut lines, self.from.render_highlighted_update(
- width, &mut highlight, None));
+ width, &mut highlight, &DefaultDisplayStyle));
push_fragment(&mut lines, self.via.render_highlighted_update(
- width, &mut highlight, None));
+ width, &mut highlight, &DefaultDisplayStyle));
push_fragment(&mut lines, self.vis.render(width));
push_fragment(&mut lines, self.irt.render(width));
push_fragment(&mut lines, self.blank.render(width));
}
if let Some(poll) = &self.poll {
push_fragment_opt_highlight(&mut lines, poll.title.render(width));
+ let poll_options_selected = style.poll_options(&poll.id);
for (i, (voted, desc)) in poll.options.iter().enumerate() {
let highlighting_this_option =
highlight.consume(HighlightType::PollOption, 1) == Some(0);
- let voted = poll_options_selected.map_or(
+ let voted = poll_options_selected.as_ref().map_or(
*voted, |opts| opts.contains(&i));
let option = Paragraph::new().set_indent(0, 2).add(
&ColouredString::uniform(
impl TextFragment for DetailedStatusDisplay {
fn render_highlighted(&self, width: usize, highlight: Option<Highlight>,
- poll_options: Option<&HashSet<usize>>)
+ style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut lines = Vec::new();
let mut highlight = highlight;
push_fragment(&mut lines, self.sd.render_highlighted_update(
- width, &mut highlight, poll_options));
+ width, &mut highlight, style));
push_fragment(&mut lines, self.sep.render(width));
push_fragment(&mut lines, self.blank.render(width));
impl TextFragment for ExamineUserDisplay {
fn render_highlighted(&self, width: usize, _highlight: Option<Highlight>,
- _poll_options: Option<&HashSet<usize>>)
+ _style: &dyn DisplayStyleGetter)
-> Vec<ColouredString>
{
let mut lines = Vec::new();