JeVois  1.23
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
Loading...
Searching...
No Matches
ChatBox.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#ifdef JEVOIS_PRO
19
20#include <jevois/GPU/ChatBox.H>
21#include <jevois/Util/Utils.H>
22#include <jevois/Component/Component.H> // only for jevois::frameNum()
23#include <imgui.h>
24#include <imgui_internal.h>
25
26// Keep this code in tight sync with jevois::GUIconsole
27
28// ##############################################################################################################
29jevois::ChatBox::ChatBox(std::string title) : itsTitle(title)
30{
31 itsInputBuf[0] = '\0';
32}
33
34// ##############################################################################################################
37
38// ##############################################################################################################
40{
41 itsData.clear();
42 itsWasCleared = true;
43}
44
45// ##############################################################################################################
47{
48 bool ret = itsWasCleared;
49 itsWasCleared = false;
50 return ret;
51}
52
53// ##############################################################################################################
55{
56 std::string str;
57
58 if (itsLastInput.empty() == false)
59 {
60 str = itsLastInput;
61
62 // Also keep a copy of the input for our display:
63 itsData.push_back(std::make_pair(true, itsLastInput));
64 itsLastInput.clear();
65 }
66
67 return str;
68}
69
70// ##############################################################################################################
71void jevois::ChatBox::writeString(std::string const & str)
72{
73 if (str.empty()) return;
74
75 // If our last data was from the bot, start a new entry for the user:
76 if (itsData.back().first) itsData.emplace_back(std::make_pair(false, std::string()));
77
78 // Concatenate to last line or create a new one?
79 size_t idx = str.find('\n');
80 if (idx == str.npos)
81 itsData.back().second += str;
82 else
83 {
84 auto tok = jevois::split(str, "\\n");
85
86 for (auto const & t : tok)
87 if (itsData.back().first) itsData.emplace_back(std::make_pair(false, t)); // start of a response
88 else { itsData.back().second += t; itsData.emplace_back(std::make_pair(false, std::string())); } // end of response
89 }
90
91 while (itsData.size() > 10000) itsData.pop_front();
92}
93
94// ##############################################################################################################
95void jevois::ChatBox::freeze(bool doit, std::string const & waitmsg)
96{
97 itsFrozen = doit;
98 itsWaitMsg = waitmsg;
99}
100
101// ##############################################################################################################
102static int TextEditCallbackStub(ImGuiInputTextCallbackData * data)
103{
104 jevois::ChatBox * console = (jevois::ChatBox *)data->UserData;
105 return console->callback(data);
106}
107
108// ##############################################################################################################
110{
111 ImGui::SetNextWindowPos(ImVec2(100, 200), ImGuiCond_FirstUseEver);
112 ImGui::SetNextWindowSize(ImVec2(1200, 800), ImGuiCond_FirstUseEver);
113
114 // Keep this in sync with GUIserial::draw() for aesthetic consistency
115 if (ImGui::Begin(itsTitle.c_str()))
116 {
117 // Reserve enough left-over height for 1 separator + 1 input text
118 const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing() + 5;
119 ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false,
120 ImGuiWindowFlags_HorizontalScrollbar);
121 ImGui::PushTextWrapPos(ImGui::GetWindowSize().x - ImGui::GetFontSize() * 2.0f);
122
123 // Right click on the log to get a popup menu that can clear it:
124 if (ImGui::BeginPopupContextWindow())
125 {
126 if (ImGui::Selectable("Clear")) clear();
127 ImGui::EndPopup();
128 }
129
130 // Tighten spacing:
131 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 1));
132
133 // Colorize and draw each data line:
134 for (auto const & p : itsData)
135 {
136 ImVec4 color; bool has_color = false;
137 auto const & s = p.second;
138
139 // Colors are RGBA
140 if (p.first) { color = ImVec4(0.8f, 0.8f, 0.2f, 1.0f); has_color = true; }
141 /*
142 else
143 {
144 if (s == "OK") { color = ImVec4(0.2f, 1.0f, 0.2f, 1.0f); has_color = true; }
145 else if (jevois::stringStartsWith(s, "DBG ")) { color = ImVec4(0.2f, 0.2f, 1.0f, 1.0f); has_color = true; }
146 else if (jevois::stringStartsWith(s, "INF ")) { color = ImVec4(0.4f, 0.7f, 0.4f, 1.0f); has_color = true; }
147 else if (jevois::stringStartsWith(s, "ERR ")) { color = ImVec4(1.0f, 0.4f, 0.4f, 1.0f); has_color = true; }
148 else if (jevois::stringStartsWith(s, "FTL ")) { color = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); has_color = true; }
149 }
150 */
151 if (has_color) ImGui::PushStyleColor(ImGuiCol_Text, color);
152 ImGui::TextUnformatted(s.c_str());
153 if (has_color) ImGui::PopStyleColor();
154 }
155
156 // If user input is frozen, display some animation showing we are working, until response starts:
157 if (itsFrozen && itsData.empty() == false && itsData.back().first)
158 {
159 int ndots = itsWaitState < 10 ? itsWaitState : 20 - itsWaitState;
160 std::string msg = std::string(10-ndots, ' ') + std::string(ndots, '.') +
161 ' ' + itsWaitMsg + ' ' + std::string(ndots, '.');
162 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
163 ImGui::TextUnformatted(msg.c_str());
164 ImGui::PopStyleColor();
165
166 if (jevois::frameNum() % 10 == 0) { ++itsWaitState; if (itsWaitState > 20) itsWaitState = 0; }
167 }
168
169 static bool autoScroll = true;
170 static bool scrollToBottom = true;
171
172 if (scrollToBottom || (autoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) ImGui::SetScrollHereY(1.0f);
173 scrollToBottom = false;
174
175 ImGui::PopStyleVar();
176 ImGui::PopTextWrapPos();
177 ImGui::EndChild();
178 ImGui::Separator();
179
180 // Command-line input:
181 std::string hint = "Type queries here...";
182 int flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackHistory;
183 if (itsFrozen)
184 {
185 ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
186 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
187 hint = "Working... Please wait...";
188 flags |= ImGuiInputTextFlags_ReadOnly;
189 }
190
191 bool reclaim_focus = false;
192 if (ImGui::InputTextWithHint("Input", hint.c_str(), itsInputBuf, IM_ARRAYSIZE(itsInputBuf),
193 flags, &TextEditCallbackStub, this))
194 {
195 itsLastInput = itsInputBuf;
196 itsInputBuf[0] = '\0';
197 reclaim_focus = true;
198 itsWaitState = 10; // reset the wait animation
199
200 // On command input, we scroll to bottom even if AutoScroll==false
201 scrollToBottom = true;
202
203 // Delete from history if we already had this command:
204 itsHistoryPos = -1;
205 if (itsHistory.empty() == false)
206 for (int i = int(itsHistory.size()) - 1; i >= 0; --i)
207 if (itsHistory[i] == itsLastInput) { itsHistory.erase(itsHistory.begin() + i); break; }
208
209 // Insert into history:
210 itsHistory.push_back(itsLastInput);
211 while (itsHistory.size() > 100) itsHistory.erase(itsHistory.begin());
212 }
213
214 // Restore any grey out:
215 if (itsFrozen)
216 {
217 ImGui::PopItemFlag();
218 ImGui::PopStyleVar();
219 }
220
221 ImGui::SameLine();
222 if (ImGui::Button("Clear chat")) clear();
223
224 // Auto-focus on window apparition
225 ImGui::SetItemDefaultFocus();
226 if (reclaim_focus) ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget
227 }
228 ImGui::End();
229}
230
231// ##############################################################################################################
232int jevois::ChatBox::callback(ImGuiInputTextCallbackData * data)
233{
234 if (data->EventFlag == ImGuiInputTextFlags_CallbackHistory)
235 {
236 const int prev_history_pos = itsHistoryPos;
237 if (data->EventKey == ImGuiKey_UpArrow)
238 {
239 if (itsHistoryPos == -1) itsHistoryPos = int(itsHistory.size()) - 1;
240 else if (itsHistoryPos > 0) --itsHistoryPos;
241 }
242 else if (data->EventKey == ImGuiKey_DownArrow)
243 {
244 if (itsHistoryPos != -1 && ++itsHistoryPos >= int(itsHistory.size())) itsHistoryPos = -1;
245 }
246
247 if (prev_history_pos != itsHistoryPos)
248 {
249 std::string const & history_str = (itsHistoryPos >= 0) ? itsHistory[itsHistoryPos] : "";
250 data->DeleteChars(0, data->BufTextLen);
251 data->InsertChars(0, history_str.c_str());
252 }
253 }
254 return 0;
255}
256
257#endif // JEVOIS_PRO
A simple helper class for a chat box rendered in ImGui.
Definition ChatBox.H:48
void freeze(bool doit, std::string const &waitmsg)
Freeze/unfreeze the input box, typically to prevent new inputs until current reply is done.
Definition ChatBox.C:95
void draw()
Render into an ImGui window.
Definition ChatBox.C:109
std::string get()
Get input string from user, or empty if no new input.
Definition ChatBox.C:54
char itsInputBuf[1024]
Definition ChatBox.H:77
bool wasCleared()
Returns true once after the user clicked the "Clear chat" button.
Definition ChatBox.C:46
ChatBox(std::string title="JeVois-Pro ChatBox")
Constructor.
Definition ChatBox.C:29
virtual ~ChatBox()
Destructor.
Definition ChatBox.C:35
int callback(ImGuiInputTextCallbackData *data)
Definition ChatBox.C:232
void writeString(std::string const &out)
Update text that is displayed above input box (output from the underlying chat bot)
Definition ChatBox.C:71
void clear()
Clear all displayed text:
Definition ChatBox.C:39
std::vector< std::string > split(std::string const &input, std::string const &regex="\\s+")
Split string into vector of tokens using a regex to specify what to split on; default regex splits by...
Definition Utils.C:270