JeVois  1.20
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
GUIconsole.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2020 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
18 
19 #ifdef JEVOIS_PRO
20 
21 #include <jevois/GPU/GUIconsole.H>
22 #include <imgui.h>
23 #include <imgui_internal.h>
24 
25 // ##############################################################################################################
26 jevois::GUIconsole::GUIconsole(std::string const & instance) :
27  jevois::UserInterface(instance)
28 {
29  itsInputBuf[0] = '\0';
30  /*
31  // "CLASSIFY" is here to provide the test case where "C"+[tab] completes to "CL" and display multiple matches.
32  Commands.push_back("HELP");
33  Commands.push_back("HISTORY");
34  Commands.push_back("CLEAR");
35  Commands.push_back("CLASSIFY");
36  */
37 }
38 
39 // ##############################################################################################################
41 { }
42 
43 // ##############################################################################################################
46 
47 // ##############################################################################################################
49 {
50  std::lock_guard<std::mutex> _(itsDataMtx);
51  itsData.clear();
52 }
53 
54 // ##############################################################################################################
55 bool jevois::GUIconsole::readSome(std::string & str)
56 {
57  std::lock_guard<std::mutex> _(itsDataMtx);
58  if (itsLastInput.empty()) return false;
59  str = itsLastInput;
60  itsLastInput.clear();
61 
62  // Also keep a copy of the input for our display:
63  itsData.push_back(std::make_pair(true, str));
64 
65  return true;
66 }
67 
68 // ##############################################################################################################
69 void jevois::GUIconsole::writeString(std::string const & str)
70 {
71  std::lock_guard<std::mutex> _(itsDataMtx);
72  itsData.push_back(std::make_pair(false, str));
73  while (itsData.size() > 10000) itsData.pop_front();
74 }
75 
76 // ##############################################################################################################
77 static int TextEditCallbackStub(ImGuiInputTextCallbackData * data)
78 {
79  jevois::GUIconsole * console = (jevois::GUIconsole *)data->UserData;
80  return console->callback(data);
81 }
82 
83 // ##############################################################################################################
85 {
86  // Reserve enough left-over height for 1 separator + 1 input text
87  const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing();
88  ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false,
89  ImGuiWindowFlags_HorizontalScrollbar);
90 
91  // Right click on the log to get a popup menu that can clear it:
92  if (ImGui::BeginPopupContextWindow())
93  {
94  if (ImGui::Selectable("Clear")) clear();
95  ImGui::EndPopup();
96  }
97 
98  // Tighten spacing:
99  ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1));
100 
101  // Colorize and draw each data line:
102  std::lock_guard<std::mutex> _(itsDataMtx);
103  for (auto const & p : itsData)
104  {
105  ImVec4 color; bool has_color = false;
106  auto const & s = p.second;
107 
108  if (p.first)
109  { color = ImVec4(1.0f, 0.8f, 0.6f, 1.0f); has_color = true; }
110  else
111  {
112  if (s == "OK") { color = ImVec4(0.2f, 1.0f, 0.2f, 1.0f); has_color = true; }
113  else if (jevois::stringStartsWith(s, "DBG ")) { color = ImVec4(0.2f, 0.2f, 1.0f, 1.0f); has_color = true; }
114  else if (jevois::stringStartsWith(s, "INF ")) { color = ImVec4(0.4f, 0.7f, 0.4f, 1.0f); has_color = true; }
115  else if (jevois::stringStartsWith(s, "ERR ")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
116  else if (jevois::stringStartsWith(s, "FTL ")) { color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); has_color = true; }
117  }
118  if (has_color) ImGui::PushStyleColor(ImGuiCol_Text, color);
119  ImGui::TextUnformatted(s.c_str());
120  if (has_color) ImGui::PopStyleColor();
121  }
122 
123  static bool autoScroll = true;
124  static bool scrollToBottom = true;
125 
126  if (scrollToBottom || (autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) ImGui::SetScrollHereY(1.0f);
127  scrollToBottom = false;
128 
129  ImGui::PopStyleVar();
130  ImGui::EndChild();
131  ImGui::Separator();
132 
133  // Command-line input:
134  bool reclaim_focus = false;
135  ImGuiInputTextFlags input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory;
136 
137  // |
138  //ImGuiInputTextFlags_CallbackCompletion
139  if (ImGui::InputTextWithHint("Input", "Type JeVois commands here...",
140  itsInputBuf, IM_ARRAYSIZE(itsInputBuf), input_text_flags,
141  &TextEditCallbackStub, this))
142  {
143  itsLastInput = itsInputBuf;
144  itsInputBuf[0] = '\0';
145  reclaim_focus = true;
146 
147  // On command input, we scroll to bottom even if AutoScroll==false
148  scrollToBottom = true;
149 
150  // Delete from history if we already had this command:
151  itsHistoryPos = -1;
152  if (itsHistory.empty() == false)
153  for (int i = int(itsHistory.size()) - 1; i >= 0; --i)
154  if (itsHistory[i] == itsLastInput) { itsHistory.erase(itsHistory.begin() + i); break; }
155 
156  // Insert into history:
157  itsHistory.push_back(itsLastInput);
158  while (itsHistory.size() > 100) itsHistory.erase(itsHistory.begin());
159  }
160 
161  // Auto-focus on window apparition
162  ImGui::SetItemDefaultFocus();
163  if (reclaim_focus) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
164 }
165 
166 // ##############################################################################################################
167 int jevois::GUIconsole::callback(ImGuiInputTextCallbackData * data)
168 {
169  switch (data->EventFlag)
170  {
171  /*
172  case ImGuiInputTextFlags_CallbackCompletion:
173  {
174  // Example of TEXT COMPLETION
175 
176  // Locate beginning of current word
177  const char* word_end = data->Buf + data->CursorPos;
178  const char* word_start = word_end;
179  while (word_start > data->Buf)
180  {
181  const char c = word_start[-1];
182  if (c == ' ' || c == '\t' || c == ',' || c == ';')
183  break;
184  word_start--;
185  }
186 
187  // Build a list of candidates
188  ImVector<const char*> candidates;
189  for (int i = 0; i < Commands.Size; i++)
190  if (Strnicmp(Commands[i], word_start, (int)(word_end - word_start)) == 0)
191  candidates.push_back(Commands[i]);
192 
193  if (candidates.Size == 0)
194  {
195  // No match
196  AddLog("No match for \"%.*s\"!\n", (int)(word_end - word_start), word_start);
197  }
198  else if (candidates.Size == 1)
199  {
200  // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing.
201  data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
202  data->InsertChars(data->CursorPos, candidates[0]);
203  data->InsertChars(data->CursorPos, " ");
204  }
205  else
206  {
207  // Multiple matches. Complete as much as we can..
208  // So inputing "C"+Tab will complete to "CL" then display "CLEAR" and "CLASSIFY" as matches.
209  int match_len = (int)(word_end - word_start);
210  for (;;)
211  {
212  int c = 0;
213  bool all_candidates_matches = true;
214  for (int i = 0; i < candidates.Size && all_candidates_matches; i++)
215  if (i == 0)
216  c = toupper(candidates[i][match_len]);
217  else if (c == 0 || c != toupper(candidates[i][match_len]))
218  all_candidates_matches = false;
219  if (!all_candidates_matches)
220  break;
221  match_len++;
222  }
223 
224  if (match_len > 0)
225  {
226  data->DeleteChars((int)(word_start - data->Buf), (int)(word_end - word_start));
227  data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len);
228  }
229 
230  // List matches
231  AddLog("Possible matches:\n");
232  for (int i = 0; i < candidates.Size; i++)
233  AddLog("- %s\n", candidates[i]);
234  }
235 
236  break;
237  }
238  */
239  case ImGuiInputTextFlags_CallbackHistory:
240  {
241  // Example of HISTORY
242  const int prev_history_pos = itsHistoryPos;
243  if (data->EventKey == ImGuiKey_UpArrow)
244  {
245  if (itsHistoryPos == -1) itsHistoryPos = int(itsHistory.size()) - 1;
246  else if (itsHistoryPos > 0) --itsHistoryPos;
247  }
248  else if (data->EventKey == ImGuiKey_DownArrow)
249  {
250  if (itsHistoryPos != -1 && ++itsHistoryPos >= int(itsHistory.size())) itsHistoryPos = -1;
251  }
252 
253  // A better implementation would preserve the data on the current input line along with cursor position.
254  if (prev_history_pos != itsHistoryPos)
255  {
256  std::string const & history_str = (itsHistoryPos >= 0) ? itsHistory[itsHistoryPos] : "";
257  data->DeleteChars(0, data->BufTextLen);
258  data->InsertChars(0, history_str.c_str());
259  }
260  }
261  }
262  return 0;
263 }
264 
265 #endif // JEVOIS_PRO
jevois::GUIconsole::readSome
bool readSome(std::string &str) override
Read some bytes if available, and return true and a string when one is complete (RETURN pressed)
Definition: GUIconsole.C:55
jevois::GUIconsole::~GUIconsole
virtual ~GUIconsole()
Destructor.
Definition: GUIconsole.C:40
jevois::UserInterface
Abstract base class for a string-based user interface.
Definition: UserInterface.H:32
jevois::GUIconsole::callback
int callback(ImGuiInputTextCallbackData *data)
Definition: GUIconsole.C:167
jevois
Definition: Concepts.dox:1
jevois::GUIconsole::clear
void clear()
Clear the contents of the window.
Definition: GUIconsole.C:48
GUIconsole.H
jevois::UserInterface::Type
Type
Enum for the interface type.
Definition: UserInterface.H:59
jevois::GUIconsole
Simple console with coloring and completion.
Definition: GUIconsole.H:31
jevois::stringStartsWith
bool stringStartsWith(std::string const &str, std::string const &prefix)
Return true if str starts with prefix (including if both strings are equal)
Definition: Utils.C:294
jevois::GUIconsole::type
Type type() const override
Our type is: GUI.
Definition: GUIconsole.C:44
jevois::GUIconsole::draw
void draw()
Render into ImGui.
Definition: GUIconsole.C:84
jevois::GUIconsole::writeString
void writeString(std::string const &str) override
Write a string.
Definition: GUIconsole.C:69
jevois::GUIconsole::GUIconsole
GUIconsole(std::string const &instance)
Constructor.
Definition: GUIconsole.C:26
jevois::UserInterface::Type::GUI
@ GUI