The Battle for Wesnoth  1.19.0-dev
policy_order.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2024
3  by Mark de Wever <koraq@xs4all.nl>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #pragma once
17 
20 #include "gui/core/log.hpp"
21 #include "gui/widgets/widget.hpp"
22 
23 #include <cassert>
24 
26 {
27 
28 template <bool VW, bool VG, bool VC>
29 class bottom_up : public visit_level<VW, walker_base::self>,
30  public visit_level<VG, walker_base::internal>,
31  public visit_level<VC, walker_base::child>
32 {
36 
37 public:
38  explicit bottom_up(widget& root) : root_(root.create_walker()), stack_()
39  {
40  TST_GUI_I << "Constructor: ";
41  while(!visit_child::at_end(*root_)) {
42  stack_.push_back(std::exchange(root_, visit_child::get(*root_)->create_walker()));
43  TST_GUI_I << " Down widget '" << operator*().id() << "'.";
44  }
45 
46  if(!at_end()) {
47  TST_GUI_I << " Finished at '" << operator*().id() << "'.\n";
48  } else {
49  TST_GUI_I << " Finished at the end.\n";
50  }
51  }
52 
54  {
55  }
56 
57  bool at_end() const
58  {
59  return visit_widget::at_end(*root_) && visit_grid::at_end(*root_)
60  && visit_child::at_end(*root_);
61  }
62 
63  bool next()
64  {
65  if(at_end()) {
66  ERR_GUI_I << "Tried to move beyond end of the iteration range.";
67  throw range_error("Tried to move beyond end of range.");
68  }
69 
70  TST_GUI_I << "At '" << operator*().id() << "'.";
71 
72  /***** WIDGET *****/
73  TST_GUI_I << " Iterate widget:";
74  if(!visit_widget::at_end(*root_)) {
75  switch(visit_widget::next(*root_)) {
76  case walker_base::valid:
77  TST_GUI_I << " visit '" << operator*().id() << "'.\n";
78  return true;
80  TST_GUI_I << " reached the end.";
81  break;
82  case walker_base::fail:
83  TST_GUI_I << "\n";
84  ERR_GUI_E << "Tried to move beyond end of "
85  "the widget iteration range.";
86  throw range_error("Tried to move beyond end of range.");
87  }
88  } else {
89  TST_GUI_I << " failed.";
90  }
91 
92  /***** GRID *****/
93  TST_GUI_I << " Iterate grid:";
94  if(!visit_grid::at_end(*root_)) {
95  switch(visit_grid::next(*root_)) {
96  case walker_base::valid:
97  TST_GUI_I << " visit '" << operator*().id() << "'.\n";
98  return true;
100  TST_GUI_I << " reached the end.";
101  break;
102  case walker_base::fail:
103  TST_GUI_I << "\n";
104  ERR_GUI_E << "Tried to move beyond end of "
105  "the grid iteration range.";
106  throw range_error("Tried to move beyond end of range.");
107  }
108  } else {
109  TST_GUI_I << " failed.";
110  }
111 
112  /***** TRAVERSE CHILDREN *****/
113 
114  TST_GUI_I << " Iterate child:";
115  if(visit_child::at_end(*root_)) {
116  if(stack_.empty()) {
117  TST_GUI_I << " Finished iteration.\n";
118  return false;
119  } else {
120  root_ = std::move(stack_.back());
121  stack_.pop_back();
122  TST_GUI_I << " Up '" << operator*().id() << "'.";
123  }
124  }
125  TST_GUI_I << " Iterate child:";
126  if(!visit_child::at_end(*root_)) {
127  switch(visit_child::next(*root_)) {
128  case walker_base::valid:
129  TST_GUI_I << " visit '" << operator*().id() << "'.";
130  break;
132  TST_GUI_I << " reached the end.";
133  break;
134  case walker_base::fail:
135  TST_GUI_I << "\n";
136  ERR_GUI_E << "Tried to move beyond end of "
137  "the child iteration range.";
138  throw range_error("Tried to move beyond end of range.");
139  }
140  } else {
141  TST_GUI_I << " already at the end.";
142  }
143 
144  while(!visit_child::at_end(*root_)) {
145  stack_.push_back(std::exchange(root_, visit_child::get(*root_)->create_walker()));
146  TST_GUI_I << " Down widget '" << operator*().id() << "'.";
147  }
148  TST_GUI_I << " Visit '" << operator*().id() << "'.\n";
149  return true;
150  }
151 
153  {
154  if(at_end()) {
155  ERR_GUI_I << "Tried to defer beyond end its "
156  "iteration range iterator.";
157  throw logic_error("Tried to defer an invalid iterator.");
158  }
159  if(!visit_widget::at_end(*root_)) {
160  return *visit_widget::get(*root_);
161  }
162  if(!visit_grid::at_end(*root_)) {
163  return *visit_grid::get(*root_);
164  }
165  if(!visit_child::at_end(*root_)) {
166  return *visit_child::get(*root_);
167  }
168  ERR_GUI_I << "The iterator ended in an unknown "
169  "state while deferring itself.";
170  throw logic_error("Tried to defer an invalid iterator.");
171  }
172 
173 private:
175 
176  std::vector<iteration::walker_ptr> stack_;
177 };
178 
179 template <bool VW, bool VG, bool VC>
180 class top_down : public visit_level<VW, walker_base::self>,
181  public visit_level<VG, walker_base::internal>,
182  public visit_level<VC, walker_base::child>
183 {
187 
188 public:
189  explicit top_down(widget& root) : root_(root.create_walker()), stack_()
190  {
191  }
192 
194  {
195  }
196 
197  bool at_end() const
198  {
199  if(!root_) return true;
200  return visit_widget::at_end(*root_) && visit_grid::at_end(*root_)
201  && visit_child::at_end(*root_);
202  }
203 
204  bool next()
205  {
206  if(at_end()) {
207  ERR_GUI_I << "Tried to move beyond end of the iteration range.";
208  throw range_error("Tried to move beyond end of range.");
209  }
210 
211  TST_GUI_I << "At '" << operator*().id() << "'.";
212 
213  /***** WIDGET *****/
214  TST_GUI_I << " Iterate widget:";
215  if(!visit_widget::at_end(*root_)) {
216  switch(visit_widget::next(*root_)) {
217  case walker_base::valid:
218  TST_GUI_I << " visit '" << operator*().id() << "'.\n";
219  return true;
221  TST_GUI_I << " reached the end.";
222  break;
223  case walker_base::fail:
224  TST_GUI_I << "\n";
225  ERR_GUI_E << "Tried to move beyond end of the "
226  "widget iteration range.";
227  throw range_error("Tried to move beyond end of range.");
228  }
229  } else {
230  TST_GUI_I << " failed.";
231  }
232 
233  /***** GRID *****/
234  TST_GUI_I << " Iterate grid:";
235  if(!visit_grid::at_end(*root_)) {
236  switch(visit_grid::next(*root_)) {
237  case walker_base::valid:
238  TST_GUI_I << " visit '" << operator*().id() << "'.\n";
239  return true;
241  TST_GUI_I << " reached the end.";
242  break;
243  case walker_base::fail:
244  TST_GUI_I << "\n";
245  ERR_GUI_E << "Tried to move beyond end of the grid "
246  "iteration range.";
247  throw range_error("Tried to move beyond end of range.");
248  }
249  } else {
250  TST_GUI_I << " failed.";
251  }
252 
253  /***** TRAVERSE CHILDREN *****/
254 
255  TST_GUI_I << " Iterate child:";
256  if(visit_child::at_end(*root_)) {
257  TST_GUI_I << " reached the end.";
258  up();
259  } else {
260  TST_GUI_I << " proceed.";
261  }
262 
263  if(!visit_child::at_end(*root_)) {
264  stack_.push_back(std::exchange(root_, visit_child::get(*root_)->create_walker()));
265 
266  assert(root_);
267  TST_GUI_I << " Down and visit '" << operator*().id() << "'.\n";
268  if(at_end()) up();
269  return true;
270  }
271 
272  TST_GUI_I << " Finished iteration.\n";
273  return false;
274  }
275 
277  {
278  if(at_end()) {
279  ERR_GUI_I << "Tried to defer beyond end of the iteration "
280  "range iterator.";
281  throw logic_error("Tried to defer an invalid iterator.");
282  }
283  if(!visit_widget::at_end(*root_)) {
284  return *visit_widget::get(*root_);
285  }
286  if(!visit_grid::at_end(*root_)) {
287  return *visit_grid::get(*root_);
288  }
289  if(!visit_child::at_end(*root_)) {
290  return *visit_child::get(*root_);
291  }
292  ERR_GUI_I << "The iterator ended in an unknown "
293  "state while deferring iteself.";
294  throw logic_error("Tried to defer an invalid iterator.");
295  }
296 
297 private:
298  bool up()
299  {
300  while(!stack_.empty()) {
301  root_ = std::move(stack_.back());
302  stack_.pop_back();
303  TST_GUI_I << " Up widget '" << operator*().id() << "'. Iterate:";
304  switch(visit_child::next(*root_)) {
305  case walker_base::valid:
306  TST_GUI_I << " reached '" << operator*().id() << "'.";
307  return true;
309  TST_GUI_I << " failed.";
310  break;
311  case walker_base::fail:
312  throw range_error("Tried to move beyond end of range.");
313  }
314  }
315  return true;
316  }
317 
319 
320  std::vector<iteration::walker_ptr> stack_;
321 };
322 
323 } // namespace gui2::iteration::policy::order
Thrown when deferring an invalid iterator.
Definition: exception.hpp:37
visit_level< VW, walker_base::self > visit_widget
std::vector< iteration::walker_ptr > stack_
visit_level< VG, walker_base::internal > visit_grid
visit_level< VC, walker_base::child > visit_child
visit_level< VC, walker_base::child > visit_child
visit_level< VG, walker_base::internal > visit_grid
std::vector< iteration::walker_ptr > stack_
visit_level< VW, walker_base::self > visit_widget
Helper class to select to visit or skip a level.
Thrown when moving an invalid iterator.
Definition: exception.hpp:56
@ invalid
When calling next the following it has the following results.
Definition: walker.hpp:69
@ valid
When calling next the following it has the following results.
Definition: walker.hpp:58
Base class for all widgets.
Definition: widget.hpp:53
const std::string & id() const
Definition: widget.cpp:110
Contains the exceptions thrown by the gui2::iteration::iterator classes.
Define the common log macros for the gui toolkit.
#define ERR_GUI_E
Definition: log.hpp:38
#define TST_GUI_I
Definition: log.hpp:48
#define ERR_GUI_I
Definition: log.hpp:52
CURSOR_TYPE get()
Definition: cursor.cpp:216
std::unique_ptr< class walker_base > walker_ptr
Definition: widget.hpp:42