Raspberry Pi Weighting Control System
This project serves as a simple weighting control system, that was realized as a Bachelor Thesis
screen_manager.cpp
Go to the documentation of this file.
1 #include "screen_manager.h"
2 
3 #include <vector>
4 #include <exception>
5 #include <spdlog/spdlog.h>
6 #include "screen_definitions.h"
7 #include "imgui.h"
8 #include "imgui_internal.h" // for access to GetItemID because of focus
9 #include "localisation.h"
10 #include "app_workspace.h"
11 
12 // for debug
13 // #include "gui_direct.h"
14 
15 // not including 0 (err_screen): 1-DESIRED_SCREEN_NUMBER
16 #define DEBUG_SCREENS 5
17 #define REAL_SCREENS 6
18 
19 // runtime "window" width (padding not included), and line height (can change for each element)
22 
23 #define PATIENT_SCREEN_CNT 4
24 #define EMPLOYEE_SCREEN_CNT 4
25 // #define ADMIN_SCREEN_CNT 5
26 const uint8_t patient_screens[] = {1, 2, 3, 4};
27 const uint8_t employee_screens[] = {1, 2, 3, 4};
28 // const uint8_t admin_screens[] = {1, 2, 3, 4, 5};
29 
30 void render_nav_bar();
31 void render_status_bar();
32 
33 
35  int desired_screen_number;
36  if (app_workspace::get_instance()->is_debug_screens())
37  desired_screen_number = DEBUG_SCREENS;
38  else
39  desired_screen_number = REAL_SCREENS;
40 
41  for (int i = 0; i <= desired_screen_number; i++) {
42  this->available_screens.emplace(i, std::vector<std::unique_ptr<gui_element>>());
43  }
44 
45  define_screen_0(available_screens.at(0));
46  if (app_workspace::get_instance()->is_debug_screens()) {
47  define_screen_1_debug(available_screens.at(1));
48  define_screen_2_debug(available_screens.at(2));
49  define_screen_3_debug(available_screens.at(3));
50  define_screen_4_debug(available_screens.at(4));
51  define_screen_5_debug(available_screens.at(5));
52  } else {
53  define_screen_1(available_screens.at(LOGIN_SCREEN));
54  define_screen_2(available_screens.at(MEASURING_SCREEN));
55  define_screen_3(available_screens.at(SELECTION_SCREEN));
56  define_screen_4(available_screens.at(DETAIL_SCREEN));
57  define_screen_5(available_screens.at(ADMIN_SELECT_SCREEN));
58  define_screen_6(available_screens.at(ADMIN_CTRL_SCREEN));
59  }
60 
61  this->selected_screen = 1; // default screen
62 }
63 
64 // FIXME : this is duplicate from file event_handler.cpp, but is only used for debug here
65 template<typename Base, typename T>
66 inline bool instanceof(const T *ptr) {
67  return dynamic_cast<const Base*>(ptr) != nullptr;
68 }
69 
70 void screen_manager::render_screen(std::vector<std::unique_ptr<gui_element>> &elements) {
71  focused = nullptr;
72  activated = nullptr;
73  ImGuiID last_focused = 0;
74 
75  for (size_t i = 0; i < elements.size(); i++) {
76  std::unique_ptr<gui_element> &element = elements.at(i);
77  element->index = i;
78  element->render_element();
79 
80  if (ImGui::IsItemFocused()) {
81  // maybe GetFocusID instead?
82  if (last_focused != ImGui::GetItemID()) {
83  focused = element.get();
84  last_focused = ImGui::GetItemID();
85  }
86  }
87  /*// debug if
88  if (ImGui::IsItemFocused()) {
89  if (instanceof<gui_direct>(element.get())) {
90  ImGui::IsItemFocused();
91  printf("Wrong item is focused\n");
92  }
93  // if (i == 7) { // this should be gui_direct and cannot be higlighted
94  // ImGui::IsItemFocused();
95  // }
96  // if (i == 6) { // this should be password input
97  // ImGui::IsItemFocused();
98  // }
99  spdlog::debug("setting focus item index {0}", i);
100  focused = element.get();
101  }
102  */
103  // activated is only true when the item was previously inactive
104  else if (ImGui::IsItemActivated() || ImGui::IsItemActive())
105  activated = element.get();
106  }
107 }
108 
109 bool screen_manager::is_screen_available_to_user(const uint8_t *screens, const uint8_t count, const uint8_t requested) {
110  for (int i = 0; i < count; i++) {
111  if (screens[i] == requested)
112  return true;
113  }
114  return false;
115 }
116 
118  //spdlog::debug("screen_manager.cpp - Rendering screen number: {0}", this->selected_screen);
119  rt_content_width = ImGui::GetWindowContentRegionMax().x;
120  rt_line_height = ImGui::GetTextLineHeightWithSpacing();
121 
122  if (app_workspace::get_instance()->refresh_current_screen) {
123  refresh_screen_elements(selected_screen);
124  app_workspace::get_instance()->refresh_current_screen = false;
125  }
126 
127  /*
128  if (ImGui::IsAnyItemActive())
129  spdlog::info("An item is ACTIVE");
130  if (ImGui::IsAnyItemFocused())
131  spdlog::info("An item is FOCUSED");
132  if (ImGui::IsAnyItemHovered())
133  spdlog::info("An item is HOVERED");
134  */
135 
138 
140  try {
141  std::vector<std::unique_ptr<gui_element>> &elements = available_screens.at(selected_screen);
142  render_screen(elements);
143  } catch (std::out_of_range &ex) {
144  spdlog::error("screen_manager.cpp - Failed to find screen key in available_screens map."
145  " Cannot display selected screen");
146  selected_screen = 0;
148  return;
149  }
150 
152  render_nav_bar();
153 }
154 
156  return this->selected_screen;
157 }
158 
160  /* not tested - for now disabled for easier testing
161  if (app_workspace::get_instance()->has_user()) {
162  if (app_workspace::get_instance()->userspace->get_role() == 0) {
163  if (!is_screen_available_to_user(patient_screens, PATIENT_SCREEN_CNT, screen)) {
164  spdlog::debug("User doesn't have permission to access screen {0}", screen);
165  return;
166  }
167  } else if (app_workspace::get_instance()->userspace->get_role() == 1) {
168  if (!is_screen_available_to_user(employee_screens, EMPLOYEE_SCREEN_CNT, screen)) {
169  spdlog::debug("User doesn't have permission to access screen {0}", screen);
170  return;
171  }
172  } else if (app_workspace::get_instance()->userspace->get_role() == 2) {
173  // no action for admin, admin has permission for all screens
174  } else {
175  spdlog::error("User role not recognized when setting screen");
176  return;
177  }
178  } else {
179  spdlog::debug("No user login, only available screen is 1");
180  if (this->selected_screen != 1) this->selected_screen = 1;
181  return;
182  }
183  */
184 
185  if (app_workspace::get_instance()->is_debug_screens()) {
186  if (screen > DEBUG_SCREENS) {
187  spdlog::debug("Discarding screen select request.");
188  return;
189  }
190  } else {
191  if (screen > REAL_SCREENS) {
192  spdlog::debug("Discarding screen select request.");
193  return;
194  }
195  }
196 
197  spdlog::info("Setting screen to {0}", screen);
199  this->selected_screen = screen;
200 }
201 
202 
204  std::vector<std::unique_ptr<gui_element>> *elems;
205 
206  try {
207  elems = &(available_screens.at(screen));
208  //elems->clear(); //clear current screen elements
209  } catch (std::invalid_argument &ex) {
210  spdlog::error("screen_manager.cpp - Cannot refresh screen {0}. Screen doesn't exist");
211  return;
212  }
213 
214  if (app_workspace::get_instance()->is_debug_screens()) {
215  switch (screen) {
216  case 0:
217  define_screen_0(*elems); break;
218  case 1:
219  define_screen_1_debug(*elems); break;
220  case 2:
221  define_screen_2_debug(*elems); break;
222  case 3:
223  define_screen_3_debug(*elems); break;
224  case 4:
225  define_screen_4_debug(*elems); break;
226  case 5:
227  define_screen_5_debug(*elems); break;
228 
229  default:
230  spdlog::error("screen_manager.cpp - Cannot refresh screen {0}. Screen doesn't exist", screen);
231  break;
232  }
233  } else {
234  switch (screen) {
235  case 0:
236  define_screen_0(*elems); break;
237  case 1:
238  define_screen_1(*elems); break;
239  case 2:
240  define_screen_2(*elems); break;
241  case 3:
242  define_screen_3(*elems); break;
243  case 4:
244  define_screen_4(*elems); break;
245  case 5:
246  define_screen_5(*elems); break;
247  case 6:
248  define_screen_6(*elems); break;
249 
250  default:
251  spdlog::error("screen_manager.cpp - Cannot refresh screen {0}. Screen doesn't exist", screen);
252  break;
253  }
254  }
255 }
256 
258  // starting from 1, because 0 (err_sreen) doesn't need to be reloaded
259  // also don't do error checking for i > 255 (refresh_screen_elements takes uint8_t)
260  for (size_t i = 1; i < available_screens.size(); i++) {
262  }
263 }
264 
266  if (prev_screen_stack.size() > PREV_SCREEN_Q_MAX)
267  prev_screen_stack.erase(prev_screen_stack.begin() + (PREV_SCREEN_Q_MAX / 2));
268  // prev_screen_stack.clear();
269 
270  // dont allow to go back to error screen
271  if (selected_screen > 0)
272  prev_screen_stack.push_back(selected_screen);
273 }
274 
276  if (prev_screen_stack.size() <= 0) {
277  spdlog::info("screen_manager.cpp - Cannot go to previous screen. No screens in stack.");
278  return;
279  }
280 
281  selected_screen = prev_screen_stack[prev_screen_stack.size() - 1];
282  prev_screen_stack.pop_back();
283 }
284 
286  prev_screen_stack.clear();
287 }
288 
289 /*********************************************************************************************************************/
290 /* private screen_manager functions */
291 /*********************************************************************************************************************/
292 
294 
295 void draw_status_circle(ImVec2 center, bool usr_logged, float radius) {
296  ImU32 color = usr_logged ? IM_COL32(0, 255, 0, 255) : IM_COL32(255, 0, 0, 255);
297 
298  ImDrawList *wdl = ImGui::GetWindowDrawList();
299  wdl->AddCircleFilled(center, radius, color);
300 }
301 
303  app_workspace *app_wrk = app_workspace::get_instance().get();
304  screen_manager *scr_mgr = app_wrk->get_scr_mgr();
305  ImVec2 win_pos = ImGui::GetWindowPos();
306  ImVec2 win_size = ImGui::GetContentRegionMax();
307  ImVec2 cur_pos;
308  ImVec2 padding = ImGui::GetStyle().WindowPadding;
309  char usr_buff[DEF_BUFF_SIZE] = {0};
310  float radius = 8;
311 
312  sprintf(usr_buff, "%s: %s", get_localized_text("GT_USER"),
313  app_wrk->has_user() ? app_wrk->userspace->get_username().c_str() :
314  get_localized_text("GT_USER_NO_LOGIN"));
315 
316  cur_pos = ImGui::GetCursorPos();
317  ImVec2 usr_center(win_pos.x + cur_pos.x + radius, win_pos.y + cur_pos.y + (radius / 2));
318 
319  // print username and draw status circle
320  draw_status_circle(usr_center, app_wrk->user_logged, radius);
321  ImGui::SetCursorPosX(padding.x + (radius * 2) + padding.x);
322  ImGui::Text(usr_buff);
323 
324  // print time
325  time_t now = time(0);
326  tm *ltm = localtime(&now);
327  char time[DEF_BUFF_SIZE_V_SMALL];
328  sprintf(time, "%.2d:%.2d:%.2d", ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
329  ImVec2 tts = ImGui::CalcTextSize(time); // time text size
330  ImGui::SameLine();
331  float time_x = win_size.x - padding.x - tts.x - 2;
332  ImGui::SetCursorPosX(time_x);
333  ImGui::Text(time);
334 
335  // print selected screen
336  // FIXME centered in previous version, moved more to right because i haven't decided yet on usr: usr_name length
337  ImGui::SameLine();
338  //ImGui::SetCursorPosX((win_size.x / 2) - padding.x - 2);
339  ImGui::SetCursorPosX(time_x - 100);
340  ImGui::TextColored(ImColor(IM_COL32(0, 180, 0, 255)), "%d", scr_mgr->get_selected_screen());
341 
342  // "extra" empty line after status bar (as padding)
343  ImGui::NewLine();
344 }
345 
351  app_workspace *app_wrk = app_workspace::get_instance().get();;
352  app_workspace_ns::kb_input_state *kbins = app_wrk->kb_in_state.get();
353  ImVec2 win_size = ImGui::GetContentRegionMax();
354  char buff[DEF_BUFF_SIZE_BIG] = {0}, in_mod[DEF_BUFF_SIZE_SMALL] = {0};
355  float th = ImGui::GetTextLineHeightWithSpacing(); // text height (with spacing)
356 
357  switch (kbins->current_mode) {
359  strcpy(in_mod, get_localized_text("GT_NAVIGATION"));
360  break;
362  switch (kbins->current_type) {
364  strcpy(in_mod, get_localized_text("GT_INPUT_INT"));
365  break;
367  strcpy(in_mod, get_localized_text("GT_INPUT_FLOAT"));
368  break;
370  strcpy(in_mod, get_localized_text("GT_INPUT_TEXT"));
371  break;
372 
374  default:
375  strcpy(in_mod, get_localized_text("GT_INPUT_ERR"));
376  break;
377  }
378  break;
379  default:
380  strcpy(in_mod, get_localized_text("GT_ERR"));
381  break;
382  }
383 
384  sprintf(buff, "%s: %s\t%s: %s\t%s: %s",
385  get_localized_text("GT_MODE"),
386  in_mod,
387  get_localized_text("GT_CAPS"),
388  app_wrk->get_instance()->capslock_flag ? get_localized_text("GT_YES") : get_localized_text("GT_NO"),
389  get_localized_text("GT_SHIFT"),
390  app_wrk->get_instance()->lshift_flag ? get_localized_text("GT_YES") : get_localized_text("GT_NO"));
391 
392  ImGui::SetCursorPosY(win_size.y - th);
393  ImGui::Text(buff);
394 }
395 
396 
398  // -1 because 0 is err_screen
399  return available_screens.size() - 1;
400 }
#define DEF_BUFF_SIZE
Definition: app_workspace.h:23
#define DEF_BUFF_SIZE_SMALL
Definition: app_workspace.h:22
#define DEF_BUFF_SIZE_V_SMALL
Definition: app_workspace.h:21
#define DEF_BUFF_SIZE_BIG
Definition: app_workspace.h:24
One of the most importat classes in the whole project. Holds variables that define the state of the a...
static std::unique_ptr< app_workspace > & get_instance()
Get the instance app_workspace which is a singleton.
std::unique_ptr< app_workspace_ns::kb_input_state > kb_in_state
Input state holder.
bool has_user()
Checks if user is logged in. This is determined by user_space being initialized and having the user_l...
screen_manager * get_scr_mgr()
Get the screen manager instance. This instance is kind of singleton. Is initalized only once on start...
bool user_logged
Flag to indicate if a user is logged in.
std::unique_ptr< user_workspace > userspace
User's workspace. Serves similar function as app_workspace but for user data.
gui_element * focused
void clear_previous_screen_stack()
void pop_screen_from_previous()
void set_selected_screen(uint8_t screen)
uint8_t get_screen_count()
void push_screen_to_previous()
gui_element * activated
void render_active_screen()
void refresh_screen_elements(uint8_t screen)
uint8_t get_selected_screen()
const char * get_localized_text(const char *key)
Get the localized text object.
void define_screen_5_debug(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 5.
void define_screen_5(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 5.
void define_screen_6(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 6.
void define_screen_1(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 1.
void define_screen_0(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 0.
void define_screen_1_debug(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 1.
void define_screen_2_debug(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 2.
void define_screen_4(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 4.
void define_screen_3_debug(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 3.
void define_screen_3(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 3.
void define_screen_2(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 2.
void define_screen_4_debug(std::vector< std::unique_ptr< gui_element >> &elems)
Definitions of gui elemnts of debug screen 4.
const uint8_t employee_screens[]
void render_status_bar()
#define REAL_SCREENS
bool instanceof(const T *ptr)
void render_nav_bar()
called nav bar, render current input mode at the bottom of the screen
#define DEBUG_SCREENS
float rt_content_width
void draw_status_circle(ImVec2 center, bool usr_logged, float radius)
float rt_line_height
const uint8_t patient_screens[]
#define LOGIN_SCREEN
#define ADMIN_SELECT_SCREEN
#define PREV_SCREEN_Q_MAX
#define DETAIL_SCREEN
#define ADMIN_CTRL_SCREEN
#define MEASURING_SCREEN
#define SELECTION_SCREEN
Structure holding the values of keyboard input mode and input type.
Definition: app_workspace.h:96