12#include <imgui_stdlib.h>
19using namespace std::literals;
23 , symbolManager(manager.getReactor().getSymbolManager())
30 for (
const auto& watch : watches) {
31 auto out =
makeTclList(watch.description, watch.exprStr, watch.format);
32 buf.appendf(
"watch=%s\n", out.getString().c_str());
45 }
else if (name ==
"watch"sv) {
52 watches.push_back(std::move(result));
61 ImGui::SetNextWindowSize(
gl::vec2{35, 15} * ImGui::GetFontSize(), ImGuiCond_FirstUseEver);
63 const auto& style = ImGui::GetStyle();
64 auto width = style.ItemSpacing.x + 2.0f * style.FramePadding.x +
ImGui::CalcTextSize(
"Examples").x;
66 int flags = ImGuiTableFlags_Resizable
67 | ImGuiTableFlags_Reorderable
68 | ImGuiTableFlags_Hideable
69 | ImGuiTableFlags_Sortable
70 | ImGuiTableFlags_RowBg
71 | ImGuiTableFlags_BordersV
72 | ImGuiTableFlags_BordersOuter
73 | ImGuiTableFlags_SizingStretchProp
74 | ImGuiTableFlags_SortTristate
75 | ImGuiTableFlags_ScrollY;
77 ImGui::TableSetupScrollFreeze(0, 1);
78 ImGui::TableSetupColumn(
"description");
79 ImGui::TableSetupColumn(
"expression");
80 ImGui::TableSetupColumn(
"format");
81 ImGui::TableSetupColumn(
"result", ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_NoHide);
82 ImGui::TableHeadersRow();
92 if (ImGui::Button(
"Add")) {
93 selectedRow = narrow<int>(watches.size());
94 watches.emplace_back();
96 im::Disabled(selectedRow < 0 || selectedRow >= narrow<int>(watches.size()), [&]{
97 if (ImGui::Button(
"Remove")) {
98 watches.erase(watches.begin() + selectedRow);
99 if (selectedRow == narrow<int>(watches.size())) {
104 ImGui::Dummy({0, 20});
105 if (ImGui::SmallButton(
"Examples")) {
106 watches.emplace_back(
107 "peek at fixed address",
110 watches.emplace_back(
111 "VDP command executing",
112 "[debug read \"VDP status regs\" 2] & 1",
114 watches.emplace_back(
115 "PSG enable-channel status",
116 "[debug read \"PSG regs\" 7]",
118 watches.emplace_back(
119 "The following 2 require an appropriate symbol file",
122 watches.emplace_back(
123 "value of 'myLabel'",
126 watches.emplace_back(
127 "peek at symbolic address",
128 "[peek16 $sym(numItems)]",
131 ImGui::Dummy({0, 0});
132 HelpMarker(R
"(The given (Tcl) expressions are continuously evaluated and displayed in a nicely formatted way.
133Press the 'Examples' button to see some examples.
136* description: optional description for the expression
137* expression: the actual Tcl expression, see below
138* format: optional format specifier, see below
139* result: this shows the result of evaluating 'expression'
141Add a new entry via the 'Add' button, then fill in the appropriate fields.
142To remove an entry, first select a row (e.g. click on the corresponding 'result' cell), then press the 'Remove' button.
144You can sort the table by clicking the column headers.
145You can reorder the columns by dragging the column headers.
146You can hide columns via the right-click context menu on a column header. For example, once configured, you may want to hide the 'expression' and 'format' columns.
148The expression can be any Tcl expression, some examples:
149* [peek 0x1234]: monitor a byte at a specific address
150* [reg SP] < 0xe000: check that stack hasn't grown too large
151 (below address 0xe000)
152If you have debug-symbols loaded (via the 'Symbol manager'), you can use them like:
153* [peek16 $sym(mySymbol)]: monitor 16-bit value at 'mySymbol'
154There's a shorthand notation to peek 8-bit values:
155* <integer> -> [peek <integer>]
156* <symbol> -> [peek $sym(<symbol>)]
158In the format column you can optionally enter a format-specifier (for the Tcl 'format' command). Some examples:
159* 0x%04x: 4-digit hex with leading zeros and '0x' prefix
160* %08b: 8-bit binary value
161* %d items: decimal value followed by the string " items"
167static void tooWideToolTip(
float available,
zstring_view str)
169 if (str.
empty())
return;
171 width += 2.0f * ImGui::GetStyle().FramePadding.x;
172 if (width >= available) {
177void ImGuiWatchExpr::refreshSymbols()
180 for (
auto& watch : watches) {
181 watch.expression.reset();
185std::expected<TclObject, std::string> ImGuiWatchExpr::evalExpr(WatchExpr& watch,
Interpreter& interp)
const
187 if (watch.exprStr.empty())
return {};
189 if (!watch.expression) {
190 if (
auto addr = symbolManager.parseSymbolOrValue(watch.exprStr)) {
192 watch.expression = TclObject(
tmpStrCat(
"[peek ", *addr,
']'));
195 watch.expression = watch.exprStr;
198 assert(watch.expression);
201 return watch.expression->eval(interp);
202 }
catch (CommandException& e) {
203 return std::unexpected(
e.getMessage());
207void ImGuiWatchExpr::drawRow(
int row)
209 auto& interp = manager.getInterpreter();
210 auto& watch = watches[row];
213 auto exprVal = evalExpr(watch, interp);
216 std::expected<TclObject, std::string> formatted;
217 if (!watch.format.getString().empty()) {
218 auto frmtCmd =
makeTclList(
"format", watch.format, exprVal ? *exprVal : TclObject(
"0"));
221 }
catch (CommandException& e) {
222 formatted = std::unexpected(
e.getMessage());
225 formatted = exprVal ? *exprVal : TclObject();
228 const auto& display = exprVal ? (formatted ? formatted->getString() : exprVal->getString())
231 if (ImGui::TableNextColumn()) {
232 auto pos = ImGui::GetCursorPos();
233 const auto& style = ImGui::GetStyle();
234 float rowHeight = 2.0f * style.FramePadding.y;
236 if (ImGui::Selectable(
"##selection", selectedRow == row,
237 ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap,
238 {0.0f, rowHeight})) {
241 ImGui::SetCursorPos(pos);
243 auto avail = ImGui::GetContentRegionAvail().x;
244 ImGui::SetNextItemWidth(-FLT_MIN);
245 ImGui::InputText(
"##desc", &watch.description);
246 tooWideToolTip(avail, watch.description);
248 if (ImGui::TableNextColumn()) {
251 auto avail = ImGui::GetContentRegionAvail().x;
252 ImGui::SetNextItemWidth(-FLT_MIN);
253 if (ImGui::InputText(
"##expr", &watch.exprStr)) {
254 watch.expression.reset();
256 tooWideToolTip(avail, watch.exprStr);
259 if (ImGui::TableNextColumn()) {
262 auto avail = ImGui::GetContentRegionAvail().x;
263 ImGui::SetNextItemWidth(-FLT_MIN);
264 auto str = std::string(watch.format.getString());
265 if (ImGui::InputText(
"##format", &str)) {
269 tooWideToolTip(avail, str);
275 if (ImGui::TableNextColumn()) {
277 auto avail = ImGui::GetContentRegionAvail().x;
280 tooWideToolTip(avail, display);
285void ImGuiWatchExpr::checkSort()
287 auto* sortSpecs = ImGui::TableGetSortSpecs();
288 if (!sortSpecs->SpecsDirty)
return;
290 sortSpecs->SpecsDirty =
false;
291 if (sortSpecs->SpecsCount == 0)
return;
292 assert(sortSpecs->SpecsCount == 1);
293 assert(sortSpecs->Specs);
294 assert(sortSpecs->Specs->SortOrder == 0);
296 switch (sortSpecs->Specs->ColumnIndex) {
301 sortUpDown_String(watches, sortSpecs, [](
const auto& item) {
return item.exprStr; });
304 sortUpDown_String(watches, sortSpecs, [](
const auto& item) {
return item.format.getString(); });
void save(ImGuiTextBuffer &buf) override
ImGuiWatchExpr(ImGuiManager &manager)
void loadStart() override
void loadLine(std::string_view name, zstring_view value) override
void paint(MSXMotherBoard *motherBoard) override
TclObject executeCommand(Interpreter &interp, bool compile=false)
Interpret this TclObject as a command and execute it.
TclObject getListIndexUnchecked(unsigned index) const
zstring_view getString() const
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
constexpr auto empty() const
auto CalcTextSize(std::string_view str)
void TextUnformatted(const std::string &str)
void Table(const char *str_id, int column, ImGuiTableFlags flags, const ImVec2 &outer_size, float inner_width, std::invocable<> auto next)
void Window(const char *name, bool *p_open, ImGuiWindowFlags flags, std::invocable<> auto next)
void StyleColor(bool active, Args &&...args)
void Child(const char *str_id, const ImVec2 &size, ImGuiChildFlags child_flags, ImGuiWindowFlags window_flags, std::invocable<> auto next)
void Disabled(bool b, std::invocable<> auto next)
void Group(std::invocable<> auto next)
void ID_for_range(std::integral auto count, std::invocable< int > auto next)
This file implemented 3 utility functions:
bool loadOnePersistent(std::string_view name, zstring_view value, C &c, const std::tuple< Elements... > &tup)
void sortUpDown_String(Range &range, const ImGuiTableSortSpecs *sortSpecs, Projection proj)
void simpleToolTip(std::string_view desc)
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
void HelpMarker(std::string_view desc)
ImU32 getColor(imColor col)
TclObject makeTclList(Args &&... args)
TemporaryString tmpStrCat(Ts &&... ts)