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