1 /***************************************************************************
2 * Copyright (C) 2008-2016 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
, std::move(color
), border
)
43 , m_item_displayer(nullptr)
44 , m_filter_predicate(nullptr)
47 , m_highlight_color(m_base_color
)
48 , m_highlight_enabled(true)
49 , m_cyclic_scroll_enabled(false)
50 , m_autocenter_cursor(false)
52 m_items
= &m_all_items
;
55 template <typename ItemT
>
56 Menu
<ItemT
>::Menu(const Menu
&rhs
)
58 , m_item_displayer(rhs
.m_item_displayer
)
59 , m_filter_predicate(rhs
.m_filter_predicate
)
60 , m_beginning(rhs
.m_beginning
)
61 , m_highlight(rhs
.m_highlight
)
62 , m_highlight_color(rhs
.m_highlight_color
)
63 , m_highlight_enabled(rhs
.m_highlight_enabled
)
64 , m_cyclic_scroll_enabled(rhs
.m_cyclic_scroll_enabled
)
65 , m_autocenter_cursor(rhs
.m_autocenter_cursor
)
66 , m_drawn_position(rhs
.m_drawn_position
)
67 , m_selected_prefix(rhs
.m_selected_prefix
)
68 , m_selected_suffix(rhs
.m_selected_suffix
)
70 // TODO: move filtered items
71 m_all_items
.reserve(rhs
.m_all_items
.size());
72 for (const auto &item
: rhs
.m_all_items
)
73 m_all_items
.push_back(item
.copy());
74 m_items
= &m_all_items
;
77 template <typename ItemT
>
78 Menu
<ItemT
>::Menu(Menu
&&rhs
)
80 , m_item_displayer(std::move(rhs
.m_item_displayer
))
81 , m_filter_predicate(std::move(rhs
.m_filter_predicate
))
82 , m_all_items(std::move(rhs
.m_all_items
))
83 , m_filtered_items(std::move(rhs
.m_filtered_items
))
84 , m_beginning(rhs
.m_beginning
)
85 , m_highlight(rhs
.m_highlight
)
86 , m_highlight_color(rhs
.m_highlight_color
)
87 , m_highlight_enabled(rhs
.m_highlight_enabled
)
88 , m_cyclic_scroll_enabled(rhs
.m_cyclic_scroll_enabled
)
89 , m_autocenter_cursor(rhs
.m_autocenter_cursor
)
90 , m_drawn_position(rhs
.m_drawn_position
)
91 , m_selected_prefix(std::move(rhs
.m_selected_prefix
))
92 , m_selected_suffix(std::move(rhs
.m_selected_suffix
))
94 if (rhs
.m_items
== &rhs
.m_all_items
)
95 m_items
= &m_all_items
;
97 m_items
= &m_filtered_items
;
100 template <typename ItemT
>
101 Menu
<ItemT
> &Menu
<ItemT
>::operator=(Menu rhs
)
103 std::swap(static_cast<Window
&>(*this), static_cast<Window
&>(rhs
));
104 std::swap(m_item_displayer
, rhs
.m_item_displayer
);
105 std::swap(m_filter_predicate
, rhs
.m_filter_predicate
);
106 std::swap(m_all_items
, rhs
.m_all_items
);
107 std::swap(m_filtered_items
, rhs
.m_filtered_items
);
108 std::swap(m_beginning
, rhs
.m_beginning
);
109 std::swap(m_highlight
, rhs
.m_highlight
);
110 std::swap(m_highlight_color
, rhs
.m_highlight_color
);
111 std::swap(m_highlight_enabled
, rhs
.m_highlight_enabled
);
112 std::swap(m_cyclic_scroll_enabled
, rhs
.m_cyclic_scroll_enabled
);
113 std::swap(m_autocenter_cursor
, rhs
.m_autocenter_cursor
);
114 std::swap(m_drawn_position
, rhs
.m_drawn_position
);
115 std::swap(m_selected_prefix
, rhs
.m_selected_prefix
);
116 std::swap(m_selected_suffix
, rhs
.m_selected_suffix
);
117 if (rhs
.m_items
== &rhs
.m_all_items
)
118 m_items
= &m_all_items
;
120 m_items
= &m_filtered_items
;
124 template <typename ItemT
> template <typename ItemDisplayerT
>
125 void Menu
<ItemT
>::setItemDisplayer(ItemDisplayerT
&&displayer
)
127 m_item_displayer
= std::forward
<ItemDisplayerT
>(displayer
);
130 template <typename ItemT
>
131 void Menu
<ItemT
>::resizeList(size_t new_size
)
133 m_all_items
.resize(new_size
);
136 template <typename ItemT
>
137 void Menu
<ItemT
>::addItem(ItemT item
, Properties::Type properties
)
139 m_all_items
.push_back(Item(std::move(item
), properties
));
142 template <typename ItemT
>
143 void Menu
<ItemT
>::addSeparator()
145 m_all_items
.push_back(Item::mkSeparator());
148 template <typename ItemT
>
149 void Menu
<ItemT
>::insertItem(size_t pos
, ItemT item
, Properties::Type properties
)
151 m_all_items
.insert(m_all_items
.begin()+pos
, Item(std::move(item
), properties
));
154 template <typename ItemT
>
155 void Menu
<ItemT
>::insertSeparator(size_t pos
)
157 m_all_items
.insert(m_all_items
.begin()+pos
, Item::mkSeparator());
160 template <typename ItemT
>
161 bool Menu
<ItemT
>::Goto(size_t y
)
163 if (!isHighlightable(m_beginning
+y
))
165 m_highlight
= m_beginning
+y
;
169 template <typename ItemT
>
170 void Menu
<ItemT
>::refresh()
172 if (m_items
->empty())
179 size_t max_beginning
= 0;
180 if (m_items
->size() > m_height
)
181 max_beginning
= m_items
->size() - m_height
;
182 m_beginning
= std::min(m_beginning
, max_beginning
);
184 // if highlighted position is off the screen, make it visible
185 m_highlight
= std::min(m_highlight
, m_beginning
+m_height
-1);
186 // if highlighted position is invalid, correct it
187 m_highlight
= std::min(m_highlight
, m_items
->size()-1);
189 if (!isHighlightable(m_highlight
))
192 if (!isHighlightable(m_highlight
))
193 scroll(Scroll::Down
);
197 const size_t end_
= m_beginning
+m_height
;
198 m_drawn_position
= m_beginning
;
199 for (; m_drawn_position
< end_
; ++m_drawn_position
, ++line
)
202 if (m_drawn_position
>= m_items
->size())
204 for (; line
< m_height
; ++line
)
205 mvwhline(m_window
, line
, 0, NC::Key::Space
, m_width
);
208 if ((*m_items
)[m_drawn_position
].isSeparator())
210 mvwhline(m_window
, line
, 0, 0, m_width
);
213 if ((*m_items
)[m_drawn_position
].isBold())
214 *this << Format::Bold
;
215 if (m_highlight_enabled
&& m_drawn_position
== m_highlight
)
217 *this << Format::Reverse
;
218 *this << m_highlight_color
;
220 mvwhline(m_window
, line
, 0, NC::Key::Space
, m_width
);
221 if ((*m_items
)[m_drawn_position
].isSelected())
222 *this << m_selected_prefix
;
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
)
230 *this << Format::NoReverse
;
232 if ((*m_items
)[m_drawn_position
].isBold())
233 *this << Format::NoBold
;
238 template <typename ItemT
>
239 void Menu
<ItemT
>::scroll(Scroll where
)
241 if (m_items
->empty())
243 size_t max_highlight
= m_items
->size()-1;
244 size_t max_beginning
= m_items
->size() < m_height
? 0 : m_items
->size()-m_height
;
245 size_t max_visible_highlight
= m_beginning
+m_height
-1;
250 if (m_highlight
<= m_beginning
&& m_highlight
> 0)
252 if (m_highlight
== 0)
254 if (m_cyclic_scroll_enabled
)
255 return scroll(Scroll::End
);
260 if (!isHighlightable(m_highlight
))
261 scroll(m_highlight
== 0 && !m_cyclic_scroll_enabled
? Scroll::Down
: Scroll::Up
);
266 if (m_highlight
>= max_visible_highlight
&& m_highlight
< max_highlight
)
268 if (m_highlight
== max_highlight
)
270 if (m_cyclic_scroll_enabled
)
271 return scroll(Scroll::Home
);
276 if (!isHighlightable(m_highlight
))
277 scroll(m_highlight
== max_highlight
&& !m_cyclic_scroll_enabled
? Scroll::Up
: Scroll::Down
);
282 if (m_cyclic_scroll_enabled
&& m_highlight
== 0)
283 return scroll(Scroll::End
);
284 if (m_highlight
< m_height
)
287 m_highlight
-= m_height
;
288 if (m_beginning
< m_height
)
291 m_beginning
-= m_height
;
292 if (!isHighlightable(m_highlight
))
293 scroll(m_highlight
== 0 && !m_cyclic_scroll_enabled
? Scroll::Down
: Scroll::Up
);
296 case Scroll::PageDown
:
298 if (m_cyclic_scroll_enabled
&& m_highlight
== max_highlight
)
299 return scroll(Scroll::Home
);
300 m_highlight
+= m_height
;
301 m_beginning
+= m_height
;
302 m_beginning
= std::min(m_beginning
, max_beginning
);
303 m_highlight
= std::min(m_highlight
, max_highlight
);
304 if (!isHighlightable(m_highlight
))
305 scroll(m_highlight
== max_highlight
&& !m_cyclic_scroll_enabled
? Scroll::Up
: Scroll::Down
);
312 if (!isHighlightable(m_highlight
))
313 scroll(Scroll::Down
);
318 m_highlight
= max_highlight
;
319 m_beginning
= max_beginning
;
320 if (!isHighlightable(m_highlight
))
325 if (m_autocenter_cursor
)
326 highlight(m_highlight
);
329 template <typename ItemT
>
330 void Menu
<ItemT
>::reset()
336 template <typename ItemT
>
337 void Menu
<ItemT
>::clear()
339 // Don't clear filter related stuff here.
341 m_filtered_items
.clear();
344 template <typename ItemT
>
345 void Menu
<ItemT
>::highlight(size_t pos
)
347 assert(pos
< m_items
->size());
349 size_t half_height
= m_height
/2;
350 if (pos
< half_height
)
353 m_beginning
= pos
-half_height
;
356 template <typename ItemT
>
357 size_t Menu
<ItemT
>::choice() const
363 template <typename ItemT
> template <typename PredicateT
>
364 void Menu
<ItemT
>::applyFilter(PredicateT
&&pred
)
366 m_filter_predicate
= std::forward
<PredicateT
>(pred
);
367 m_filtered_items
.clear();
369 for (const auto &item
: m_all_items
)
370 if (m_filter_predicate(item
))
371 m_filtered_items
.push_back(item
);
373 m_items
= &m_filtered_items
;
376 template <typename ItemT
>
377 void Menu
<ItemT
>::reapplyFilter()
379 applyFilter(m_filter_predicate
);
382 template <typename ItemT
> template <typename TargetT
>
383 const TargetT
*Menu
<ItemT
>::filterPredicate() const
385 return m_filter_predicate
.template target
<TargetT
>();
388 template <typename ItemT
>
389 void Menu
<ItemT
>::clearFilter()
391 m_filter_predicate
= nullptr;
392 m_filtered_items
.clear();
393 m_items
= &m_all_items
;
398 #endif // NCMPCPP_MENU_IMPL_H