The Battle for Wesnoth  1.17.4+dev
policy_order.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2022
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 <iostream>
24 #include <cassert>
25 
27 {
28 
29 template <bool VW, bool VG, bool VC>
30 class bottom_up : public visit_level<VW, walker_base::self>,
31  public visit_level<VG, walker_base::internal>,
32  public visit_level<VC, walker_base::child>
33 {
37 
38 public:
39  explicit bottom_up(widget& root) : root_(root.create_walker()), stack_()
40  {
41  TST_GUI_I << "Constructor: ";
42  while(!visit_child::at_end(*root_)) {
43  stack_.push_back(std::exchange(root_, visit_child::get(*root_)->create_walker()));
44  TST_GUI_I << " Down widget '" << operator*().id() << "'.";
45  }
46 
47  if(!at_end()) {
48  TST_GUI_I << " Finished at '" << operator*().id() << "'.\n";
49  } else {
50  TST_GUI_I << " Finished at the end.\n";
51  }
52  }
53 
55  {
56  }
57 
58  bool at_end() const
59  {
60  return visit_widget::at_end(*root_) && visit_grid::at_end(*root_)
61  && visit_child::at_end(*root_);
62  }
63 
64  bool next()
65  {
66  if(at_end()) {
67  ERR_GUI_I << "Tried to move beyond end of the iteration range."
68  << std::endl;
69  throw range_error("Tried to move beyond end of range.");
70  }
71 
72  TST_GUI_I << "At '" << operator*().id() << "'.";
73 
74  /***** WIDGET *****/
75  TST_GUI_I << " Iterate widget:";
76  if(!visit_widget::at_end(*root_)) {
77  switch(visit_widget::next(*root_)) {
78  case walker_base::valid:
79  TST_GUI_I << " visit '" << operator*().id() << "'.\n";
80  return true;
82  TST_GUI_I << " reached the end.";
83  break;
84  case walker_base::fail:
85  TST_GUI_I << "\n";
86  ERR_GUI_E << "Tried to move beyond end of "
87  "the widget iteration range.\n";
88  throw range_error("Tried to move beyond end of range.");
89  }
90  } else {
91  TST_GUI_I << " failed.";
92  }
93 
94  /***** GRID *****/
95  TST_GUI_I << " Iterate grid:";
96  if(!visit_grid::at_end(*root_)) {
97  switch(visit_grid::next(*root_)) {
98  case walker_base::valid:
99  TST_GUI_I << " visit '" << operator*().id() << "'.\n";
100  return true;
102  TST_GUI_I << " reached the end.";
103  break;
104  case walker_base::fail:
105  TST_GUI_I << "\n";
106  ERR_GUI_E << "Tried to move beyond end of "
107  "the grid iteration range.\n";
108  throw range_error("Tried to move beyond end of range.");
109  }
110  } else {
111  TST_GUI_I << " failed.";
112  }
113 
114  /***** TRAVERSE CHILDREN *****/
115 
116  TST_GUI_I << " Iterate child:";
117  if(visit_child::at_end(*root_)) {
118  if(stack_.empty()) {
119  TST_GUI_I << " Finished iteration.\n";
120  return false;
121  } else {
122  root_ = std::move(stack_.back());
123  stack_.pop_back();
124  TST_GUI_I << " Up '" << operator*().id() << "'.";
125  }
126  }
127  TST_GUI_I << " Iterate child:";
128  if(!visit_child::at_end(*root_)) {
129  switch(visit_child::next(*root_)) {
130  case walker_base::valid:
131  TST_GUI_I << " visit '" << operator*().id() << "'.";
132  break;
134  TST_GUI_I << " reached the end.";
135  break;
136  case walker_base::fail:
137  TST_GUI_I << "\n";
138  ERR_GUI_E << "Tried to move beyond end of "
139  "the child iteration range.\n";
140  throw range_error("Tried to move beyond end of range.");
141  }
142  } else {
143  TST_GUI_I << " already at the end.";
144  }
145 
146  while(!visit_child::at_end(*root_)) {
147  stack_.push_back(std::exchange(root_, visit_child::get(*root_)->create_walker()));
148  TST_GUI_I << " Down widget '" << operator*().id() << "'.";
149  }
150  TST_GUI_I << " Visit '" << operator*().id() << "'.\n";
151  return true;
152  }
153 
155  {
156  if(at_end()) {
157  ERR_GUI_I << "Tried to defer beyond end its "
158  "iteration range iterator.\n";
159  throw logic_error("Tried to defer an invalid iterator.");
160  }
161  if(!visit_widget::at_end(*root_)) {
162  return *visit_widget::get(*root_);
163  }
164  if(!visit_grid::at_end(*root_)) {
165  return *visit_grid::get(*root_);
166  }
167  if(!visit_child::at_end(*root_)) {
168  return *visit_child::get(*root_);
169  }
170  ERR_GUI_I << "The iterator ended in an unknown "
171  "state while deferring itself.\n";
172  throw logic_error("Tried to defer an invalid iterator.");
173  }
174 
175 private:
177 
178  std::vector<iteration::walker_ptr> stack_;
179 };
180 
181 template <bool VW, bool VG, bool VC>
182 class top_down : public visit_level<VW, walker_base::self>,
183  public visit_level<VG, walker_base::internal>,
184  public visit_level<VC, walker_base::child>
185 {
189 
190 public:
191  explicit top_down(widget& root) : root_(root.create_walker()), stack_()
192  {
193  }
194 
196  {
197  }
198 
199  bool at_end() const
200  {
201  return visit_widget::at_end(*root_) && visit_grid::at_end(*root_)
202  && visit_child::at_end(*root_);
203  }
204 
205  bool next()
206  {
207  if(at_end()) {
208  ERR_GUI_I << "Tried to move beyond end of the iteration range."
209  << std::endl;
210  throw range_error("Tried to move beyond end of range.");
211  }
212 
213  TST_GUI_I << "At '" << operator*().id() << "'.";
214 
215  /***** WIDGET *****/
216  TST_GUI_I << " Iterate widget:";
217  if(!visit_widget::at_end(*root_)) {
218  switch(visit_widget::next(*root_)) {
219  case walker_base::valid:
220  TST_GUI_I << " visit '" << operator*().id() << "'.\n";
221  return true;
223  TST_GUI_I << " reached the end.";
224  break;
225  case walker_base::fail:
226  TST_GUI_I << "\n";
227  ERR_GUI_E << "Tried to move beyond end of the "
228  "widget iteration range.\n";
229  throw range_error("Tried to move beyond end of range.");
230  }
231  } else {
232  TST_GUI_I << " failed.";
233  }
234 
235  /***** GRID *****/
236  TST_GUI_I << " Iterate grid:";
237  if(!visit_grid::at_end(*root_)) {
238  switch(visit_grid::next(*root_)) {
239  case walker_base::valid:
240  TST_GUI_I << " visit '" << operator*().id() << "'.\n";
241  return true;
243  TST_GUI_I << " reached the end.";
244  break;
245  case walker_base::fail:
246  TST_GUI_I << "\n";
247  ERR_GUI_E << "Tried to move beyond end of the grid "
248  "iteration range.\n";
249  throw range_error("Tried to move beyond end of range.");
250  }
251  } else {
252  TST_GUI_I << " failed.";
253  }
254 
255  /***** TRAVERSE CHILDREN *****/
256 
257  TST_GUI_I << " Iterate child:";
258  if(visit_child::at_end(*root_)) {
259  TST_GUI_I << " reached the end.";
260  up();
261  } else {
262  TST_GUI_I << " proceed.";
263  }
264 
265  if(!visit_child::at_end(*root_)) {
266  stack_.push_back(std::exchange(root_, visit_child::get(*root_)->create_walker()));
267 
268  assert(root_);
269  assert(!at_end());
270  TST_GUI_I << " Down and visit '" << operator*().id() << "'.\n";
271  return true;
272  }
273 
274  TST_GUI_I << " Finished iteration.\n";
275  return false;
276  }
277 
279  {
280  if(at_end()) {
281  ERR_GUI_I << "Tried to defer beyond end of the iteration "
282  "range iterator.\n";
283  throw logic_error("Tried to defer an invalid iterator.");
284  }
285  if(!visit_widget::at_end(*root_)) {
286  return *visit_widget::get(*root_);
287  }
288  if(!visit_grid::at_end(*root_)) {
289  return *visit_grid::get(*root_);
290  }
291  if(!visit_child::at_end(*root_)) {
292  return *visit_child::get(*root_);
293  }
294  ERR_GUI_I << "The iterator ended in an unknown "
295  "state while deferring iteself.\n";
296  throw logic_error("Tried to defer an invalid iterator.");
297  }
298 
299 private:
300  bool up()
301  {
302  while(!stack_.empty()) {
303  root_ = std::move(stack_.back());
304  stack_.pop_back();
305  TST_GUI_I << " Up widget '" << operator*().id() << "'. Iterate:";
306  switch(visit_child::next(*root_)) {
307  case walker_base::valid:
308  TST_GUI_I << " reached '" << operator*().id() << "'.";
309  return true;
311  TST_GUI_I << " failed.";
312  break;
313  case walker_base::fail:
314  throw range_error("Tried to move beyond end of range.");
315  }
316  }
317  return true;
318  }
319 
321 
322  std::vector<iteration::walker_ptr> stack_;
323 };
324 
325 } // namespace gui2::iteration::policy::order
Define the common log macros for the gui toolkit.
#define ERR_GUI_E
Definition: log.hpp:38
std::unique_ptr< class walker_base > walker_ptr
Definition: widget.hpp:39
When calling next the following it has the following results.
Definition: walker.hpp:69
When calling next the following it has the following results.
Definition: walker.hpp:58
const std::string & id() const
Definition: widget.cpp:111
Helper class to select to visit or skip a level.
Base class for all widgets.
Definition: widget.hpp:49
Contains the exceptions thrown by the gui2::iteration::iterator classes.
visit_level< VW, walker_base::self > visit_widget
visit_level< VC, walker_base::child > visit_child
visit_level< VW, walker_base::self > visit_widget
std::vector< iteration::walker_ptr > stack_
Thrown when moving an invalid iterator.
Definition: exception.hpp:54
#define TST_GUI_I
Definition: log.hpp:48
visit_level< VC, walker_base::child > visit_child
CURSOR_TYPE get()
Definition: cursor.cpp:216
visit_level< VG, walker_base::internal > visit_grid
#define ERR_GUI_I
Definition: log.hpp:52
Thrown when deferring an invalid iterator.
Definition: exception.hpp:36
std::vector< iteration::walker_ptr > stack_
visit_level< VG, walker_base::internal > visit_grid