1 /***************************************************************************
2 * Copyright (C) 2008-2017 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #ifndef NCMPCPP_MENU_IMPL_H
22 #define NCMPCPP_MENU_IMPL_H
28 template <typename ItemT
>
31 m_items
= &m_all_items
;
34 template <typename ItemT
>
35 Menu
<ItemT
>::Menu(size_t startx
,
39 const std::string
&title
,
42 : Window(startx
, starty
, width
, height
, title
, color
, border
)
43 , m_item_displayer(nullptr)
44 , m_filter_predicate(nullptr)
47 , m_highlight_enabled(true)
48 , m_cyclic_scroll_enabled(false)
49 , m_autocenter_cursor(false)
51 auto fc
= FormattedColor(m_base_color
, {Format::Reverse
});
52 m_highlight_prefix
<< fc
;
53 m_highlight_suffix
<< FormattedColor::End
<>(fc
);
54 m_items
= &m_all_items
;
57 template <typename ItemT
>
58 Menu
<ItemT
>::Menu(const Menu
&rhs
)
60 , m_item_displayer(rhs
.m_item_displayer
)
61 , m_filter_predicate(rhs
.m_filter_predicate
)
62 , m_beginning(rhs
.m_beginning
)
63 , m_highlight(rhs
.m_highlight
)
64 , m_highlight_enabled(rhs
.m_highlight_enabled
)
65 , m_cyclic_scroll_enabled(rhs
.m_cyclic_scroll_enabled
)
66 , m_autocenter_cursor(rhs
.m_autocenter_cursor
)
67 , m_drawn_position(rhs
.m_drawn_position
)
68 , m_highlight_prefix(rhs
.m_highlight_prefix
)
69 , m_highlight_suffix(rhs
.m_highlight_suffix
)
70 , m_selected_prefix(rhs
.m_selected_prefix
)
71 , m_selected_suffix(rhs
.m_selected_suffix
)
73 // TODO: move filtered items
74 m_all_items
.reserve(rhs
.m_all_items
.size());
75 for (const auto &item
: rhs
.m_all_items
)
76 m_all_items
.push_back(item
.copy());
77 m_items
= &m_all_items
;
80 template <typename ItemT
>
81 Menu
<ItemT
>::Menu(Menu
&&rhs
)
83 , m_item_displayer(std::move(rhs
.m_item_displayer
))
84 , m_filter_predicate(std::move(rhs
.m_filter_predicate
))
85 , m_all_items(std::move(rhs
.m_all_items
))
86 , m_filtered_items(std::move(rhs
.m_filtered_items
))
87 , m_beginning(rhs
.m_beginning
)
88 , m_highlight(rhs
.m_highlight
)
89 , m_highlight_enabled(rhs
.m_highlight_enabled
)
90 , m_cyclic_scroll_enabled(rhs
.m_cyclic_scroll_enabled
)
91 , m_autocenter_cursor(rhs
.m_autocenter_cursor
)
92 , m_drawn_position(rhs
.m_drawn_position
)
93 , m_highlight_prefix(std::move(rhs
.m_highlight_prefix
))
94 , m_highlight_suffix(std::move(rhs
.m_highlight_suffix
))
95 , m_selected_prefix(std::move(rhs
.m_selected_prefix
))
96 , m_selected_suffix(std::move(rhs
.m_selected_suffix
))
98 if (rhs
.m_items
== &rhs
.m_all_items
)
99 m_items
= &m_all_items
;
101 m_items
= &m_filtered_items
;
104 template <typename ItemT
>
105 Menu
<ItemT
> &Menu
<ItemT
>::operator=(Menu rhs
)
107 std::swap(static_cast<Window
&>(*this), static_cast<Window
&>(rhs
));
108 std::swap(m_item_displayer
, rhs
.m_item_displayer
);
109 std::swap(m_filter_predicate
, rhs
.m_filter_predicate
);
110 std::swap(m_all_items
, rhs
.m_all_items
);
111 std::swap(m_filtered_items
, rhs
.m_filtered_items
);
112 std::swap(m_beginning
, rhs
.m_beginning
);
113 std::swap(m_highlight
, rhs
.m_highlight
);
114 std::swap(m_highlight_enabled
, rhs
.m_highlight_enabled
);
115 std::swap(m_cyclic_scroll_enabled
, rhs
.m_cyclic_scroll_enabled
);
116 std::swap(m_autocenter_cursor
, rhs
.m_autocenter_cursor
);
117 std::swap(m_drawn_position
, rhs
.m_drawn_position
);
118 std::swap(m_highlight_prefix
, rhs
.m_highlight_prefix
);
119 std::swap(m_highlight_suffix
, rhs
.m_highlight_suffix
);
120 std::swap(m_selected_prefix
, rhs
.m_selected_prefix
);
121 std::swap(m_selected_suffix
, rhs
.m_selected_suffix
);
122 if (rhs
.m_items
== &rhs
.m_all_items
)
123 m_items
= &m_all_items
;
125 m_items
= &m_filtered_items
;
129 template <typename ItemT
> template <typename ItemDisplayerT
>
130 void Menu
<ItemT
>::setItemDisplayer(ItemDisplayerT
&&displayer
)
132 m_item_displayer
= std::forward
<ItemDisplayerT
>(displayer
);
135 template <typename ItemT
>
136 void Menu
<ItemT
>::resizeList(size_t new_size
)
138 m_all_items
.resize(new_size
);
141 template <typename ItemT
>
142 void Menu
<ItemT
>::addItem(ItemT item
, Properties::Type properties
)
144 m_all_items
.push_back(Item(std::move(item
), properties
));
147 template <typename ItemT
>
148 void Menu
<ItemT
>::addSeparator()
150 m_all_items
.push_back(Item::mkSeparator());
153 template <typename ItemT
>
154 void Menu
<ItemT
>::insertItem(size_t pos
, ItemT item
, Properties::Type properties
)
156 m_all_items
.insert(m_all_items
.begin()+pos
, Item(std::move(item
), properties
));
159 template <typename ItemT
>
160 void Menu
<ItemT
>::insertSeparator(size_t pos
)
162 m_all_items
.insert(m_all_items
.begin()+pos
, Item::mkSeparator());
165 template <typename ItemT
>
166 bool Menu
<ItemT
>::Goto(size_t y
)
168 if (!isHighlightable(m_beginning
+y
))
170 m_highlight
= m_beginning
+y
;
174 template <typename ItemT
>
175 void Menu
<ItemT
>::refresh()
177 if (m_items
->empty())
184 size_t max_beginning
= 0;
185 if (m_items
->size() > m_height
)
186 max_beginning
= m_items
->size() - m_height
;
187 m_beginning
= std::min(m_beginning
, max_beginning
);
189 // if highlighted position is off the screen, make it visible
190 m_highlight
= std::min(m_highlight
, m_beginning
+m_height
-1);
191 // if highlighted position is invalid, correct it
192 m_highlight
= std::min(m_highlight
, m_items
->size()-1);
194 if (!isHighlightable(m_highlight
))
197 if (!isHighlightable(m_highlight
))
198 scroll(Scroll::Down
);
202 const size_t end_
= m_beginning
+m_height
;
203 m_drawn_position
= m_beginning
;
204 for (; m_drawn_position
< end_
; ++m_drawn_position
, ++line
)
207 if (m_drawn_position
>= m_items
->size())
209 for (; line
< m_height
; ++line
)
210 mvwhline(m_window
, line
, 0, NC::Key::Space
, m_width
);
213 if ((*m_items
)[m_drawn_position
].isSeparator())
215 mvwhline(m_window
, line
, 0, 0, m_width
);
218 if (m_highlight_enabled
&& m_drawn_position
== m_highlight
)
219 *this << m_highlight_prefix
;
220 if ((*m_items
)[m_drawn_position
].isSelected())
221 *this << m_selected_prefix
;
222 *this << NC::TermManip::ClearToEOL
;
223 if (m_item_displayer
)
224 m_item_displayer(*this);
225 if ((*m_items
)[m_drawn_position
].isSelected())
226 *this << m_selected_suffix
;
227 if (m_highlight_enabled
&& m_drawn_position
== m_highlight
)
228 *this << m_highlight_suffix
;
233 template <typename ItemT
>
234 void Menu
<ItemT
>::scroll(Scroll where
)
236 if (m_items
->empty())
238 size_t max_highlight
= m_items
->size()-1;
239 size_t max_beginning
= m_items
->size() < m_height
? 0 : m_items
->size()-m_height
;
240 size_t max_visible_highlight
= m_beginning
+m_height
-1;
245 if (m_highlight
<= m_beginning
&& m_highlight
> 0)
247 if (m_highlight
== 0)
249 if (m_cyclic_scroll_enabled
)
250 return scroll(Scroll::End
);
255 if (!isHighlightable(m_highlight
))
256 scroll(m_highlight
== 0 && !m_cyclic_scroll_enabled
? Scroll::Down
: Scroll::Up
);
261 if (m_highlight
>= max_visible_highlight
&& m_highlight
< max_highlight
)
263 if (m_highlight
== max_highlight
)
265 if (m_cyclic_scroll_enabled
)
266 return scroll(Scroll::Home
);
271 if (!isHighlightable(m_highlight
))
272 scroll(m_highlight
== max_highlight
&& !m_cyclic_scroll_enabled
? Scroll::Up
: Scroll::Down
);
277 if (m_cyclic_scroll_enabled
&& m_highlight
== 0)
278 return scroll(Scroll::End
);
279 if (m_highlight
< m_height
)
282 m_highlight
-= m_height
;
283 if (m_beginning
< m_height
)
286 m_beginning
-= m_height
;
287 if (!isHighlightable(m_highlight
))
288 scroll(m_highlight
== 0 && !m_cyclic_scroll_enabled
? Scroll::Down
: Scroll::Up
);
291 case Scroll::PageDown
:
293 if (m_cyclic_scroll_enabled
&& m_highlight
== max_highlight
)
294 return scroll(Scroll::Home
);
295 m_highlight
+= m_height
;
296 m_beginning
+= m_height
;
297 m_beginning
= std::min(m_beginning
, max_beginning
);
298 m_highlight
= std::min(m_highlight
, max_highlight
);
299 if (!isHighlightable(m_highlight
))
300 scroll(m_highlight
== max_highlight
&& !m_cyclic_scroll_enabled
? Scroll::Up
: Scroll::Down
);
307 if (!isHighlightable(m_highlight
))
308 scroll(Scroll::Down
);
313 m_highlight
= max_highlight
;
314 m_beginning
= max_beginning
;
315 if (!isHighlightable(m_highlight
))
320 if (m_autocenter_cursor
)
321 highlight(m_highlight
);
324 template <typename ItemT
>
325 void Menu
<ItemT
>::reset()
331 template <typename ItemT
>
332 void Menu
<ItemT
>::clear()
334 // Don't clear filter related stuff here.
336 m_filtered_items
.clear();
339 template <typename ItemT
>
340 void Menu
<ItemT
>::highlight(size_t pos
)
342 assert(pos
< m_items
->size());
344 size_t half_height
= m_height
/2;
345 if (pos
< half_height
)
348 m_beginning
= pos
-half_height
;
351 template <typename ItemT
>
352 size_t Menu
<ItemT
>::choice() const
358 template <typename ItemT
> template <typename PredicateT
>
359 void Menu
<ItemT
>::applyFilter(PredicateT
&&pred
)
361 m_filter_predicate
= std::forward
<PredicateT
>(pred
);
362 m_filtered_items
.clear();
364 for (const auto &item
: m_all_items
)
365 if (m_filter_predicate(item
))
366 m_filtered_items
.push_back(item
);
368 m_items
= &m_filtered_items
;
371 template <typename ItemT
>
372 void Menu
<ItemT
>::reapplyFilter()
374 applyFilter(m_filter_predicate
);
377 template <typename ItemT
> template <typename TargetT
>
378 const TargetT
*Menu
<ItemT
>::filterPredicate() const
380 return m_filter_predicate
.template target
<TargetT
>();
383 template <typename ItemT
>
384 void Menu
<ItemT
>::clearFilter()
386 m_filter_predicate
= nullptr;
387 m_filtered_items
.clear();
388 m_items
= &m_all_items
;
393 #endif // NCMPCPP_MENU_IMPL_H