36using namespace std::literals;
51static const char icons_compressed_base85[1815+1] =
52 "7])#######;9u/('/###W),##.f1$#Q6>##%[n42)[KU%G5)=-<NE/1aNV=BZrPSb]->>#ICi9.o`(*HGsO2(b6O3L+lQS%,5LsCC,H3AnAMeNA*&#Gb';9Cs3BMNvSN`sr1dD4Eo3R/"
53 "w/)8eXHkG2V3dW.;h^`I@cj`W)a4&>ZW%q/o6Q<Bou@GMQuuoJH`;-*0iR/Go%fr6jMWVRReK?-qmnUCC'Hv'<J]p]`;`Y5@C=GH?o6O+j#)*Mp]GFlQ#;0xfLQ7wj#O?DX-@1>F%"
54 "5[)<#8JFgL6SgV-xew@'uB5X7If]8QR3O%b'kS`a)%vW-iXx6*BfW>-'lB#$1fs-$R'Mk+(lB#$ckf&,peu.:J/PQ'-MYY#jFs9)*lXJgaDe8Qw(Y:v47Y&#*r[w'WQ1x'1/V:m^U4+3"
55 "7GJcM604,NJ:-##O]-x-e:k(NX@$##]jP]4ACh^X0(^fLHl/o#g]L7#N'+&#L)Qv$t@0+*I5^+4]77<.aQ9k0$g`,3$+h,3Pqji06rs-$=uG=3tq$>MOAQY>@'.`&K8P>#g>AvL9q*<#"
56 ")EEjLr^PA#m.Us-KRb'4g)Cb4%IuD#GKRL2KkRP/N7R20t3wK#-8/$jJA5U;0viS&[5AC>uIVS%3^oh24GbM(hIL>#xP%U#WE323lpd].B[A30GK_+,sX/D=dn5q&0####GBO&#.1d7$"
57 "3g1$#prgo.-<Tv->;gF4LZ>x#<)]L(/p^I*^nr?#&Y@C#cF&@'wvPJ(?nU^#pRpQ0gF*j0EPtdVN'+9%k*l)*0qi<%>=+dVKrdx0Iu(kLgk4DWX?/YGZv9dMb/q8/+fMO#W+WBOM>#`Q"
58 "xR--3bO1F*.D,G4rFuG-hwhH-AI7q.3B3I):[(cJ[pl6&]3KI%)B1se0fv]M6T3kuGSoBJNCZY#q+er?Y,8v,5cNY5*`$s$#]YV-[@i?#=@[s$G+TC4P#l8./=]s$1Pev6jlje3&-Xf-"
59 "e](jr145d3?;Rv$ZUvC%h5%fqxC$Y@8^CY$O,@H),W'Z-W-Rh27<C[Krsf;$.wg<L9br,4%.6v,>=+dVIIx:/@JJH)Z:Nu.iUkJ2tpPm:t0(Q$@OEp%Upn;%j&5QCa)iS0%5YY#IX$_]"
60 "2^x^]Zrju5Fhf-*i+YV-6'PA#(cK+*mtq;/qwkj1?f6<.0MbI),VW/23>%&4m$ie3%&AA4l'eq7+jkA#dlfj-&$+&^S8VTd.^AN-,CeM0l,'hoodPir`IofLX=$##5C.%T=UYF%^Yk*$"
61 "/)m<-]'LS-&%.20'>uu#OYU7#euQ['GPSw8P`&:)AF14+&YF&#WW`>$Mbk-$I&l-$Bp0.$eo4wLW(I3#Ys%(#U-4&#MXN1#EIc>#ik*$M0xSfLRq)$#@>[A-$Mg;-'Spr-gN<;Ni;-##"
62 "Jer=--`/,Mv_d##tg`=-5X`=-8sG<-*fG<-+C`T.3f1$#mrG<-8X`=-/#XN0Ht*A#e5>##_RFgLX%1kLF=`T.3xL$#)6xU.R,_'#RA`T.T5$C#<rG<-M2(@-xrG<-DKx>-#EfT%8nT`3"
63 "]kU.G-i@+H=DpTCuAo6D<B?hD-rI+HXhdxFL7-AFY'auGt$EJ1m,(@'un@VHdt6L#s[DYGB0-gD.X`'/4+auGpkn+H5-xF-^%/(#B_=?-kQU@-QRt7/uJ6(#Cm[*b[^es/c:@bHdN4/#"
64 "d>?L-NNei.Fn@-#afh]%mxtLF//5UCvs.>B,(rE-i<cM:^0[eF/1u'#.mk.#[5C/#-)IqLR`4rL;wXrLX.lrL1x.qLf.K-#J#`5/%$S-#c7]nLNM-X.Q####F/LMTxS&=u<7L8#";
66static constexpr ImWchar DEBUGGER_ICON_MIN = 0xead1;
67static constexpr ImWchar DEBUGGER_ICON_MAX = 0xeb8f;
68static constexpr ImWchar DEBUGGER_ICON_RUN = 0xead3;
69static constexpr ImWchar DEBUGGER_ICON_BREAK = 0xead1;
70static constexpr ImWchar DEBUGGER_ICON_STEP_IN = 0xead4;
71static constexpr ImWchar DEBUGGER_ICON_STEP_OUT = 0xead5;
72static constexpr ImWchar DEBUGGER_ICON_STEP_OVER = 0xead6;
73static constexpr ImWchar DEBUGGER_ICON_STEP_BACK = 0xeb8f;
77 auto& io = ImGui::GetIO();
78 ImFontConfig icons_config; icons_config.MergeMode =
true; icons_config.PixelSnapH =
true;
79 static constexpr std::array<ImWchar, 3>
ranges = {DEBUGGER_ICON_MIN, DEBUGGER_ICON_MAX, 0};
80 io.Fonts->AddFontFromMemoryCompressedBase85TTF(icons_compressed_base85, 20.0f, &icons_config,
ranges.data());
85 for (
auto& d : disassemblyViewers) {
91static void openOrCreate(
ImGuiManager& manager, std::vector<std::unique_ptr<T>>& viewers)
94 if (
auto it =
ranges::find(viewers,
false, &T::show); it != viewers.end()) {
99 viewers.push_back(std::make_unique<T>(manager, viewers.size()));
106 buf.appendf(
"disassemblyViewers=%d\n", narrow<int>(disassemblyViewers.size()));
107 buf.appendf(
"bitmapViewers=%d\n", narrow<int>(bitmapViewers.size()));
108 buf.appendf(
"tileViewers=%d\n", narrow<int>(tileViewers.size()));
109 buf.appendf(
"spriteViewers=%d\n", narrow<int>(spriteViewers.size()));
112 auto it = hexEditors.begin();
113 auto et = hexEditors.end();
116 auto name = std::string((*it)->getDebuggableName());
120 }
while (it != et && (*it)->getDebuggableName() == name);
121 buf.appendf(
"hexEditor.%s=%d\n", name.c_str(), count);
127 disassemblyViewers.clear();
128 bitmapViewers.clear();
130 spriteViewers.clear();
136 static constexpr std::string_view hexEditorPrefix =
"hexEditor.";
140 }
else if (name ==
"disassemblyViewers") {
141 if (
auto r = StringOp::stringTo<unsigned>(value)) {
142 repeat(*r, [&] { openOrCreate(
manager, disassemblyViewers); });
144 }
else if (name ==
"bitmapViewers") {
145 if (
auto r = StringOp::stringTo<unsigned>(value)) {
148 }
else if (name ==
"tileViewers") {
149 if (
auto r = StringOp::stringTo<unsigned>(value)) {
152 }
else if (name ==
"spriteViewers") {
153 if (
auto r = StringOp::stringTo<unsigned>(value)) {
156 }
else if (name.starts_with(hexEditorPrefix)) {
157 if (
auto r = StringOp::stringTo<unsigned>(value)) {
158 auto debuggableName = std::string(name.substr(hexEditorPrefix.size()));
160 auto index = std::distance(b, e);
161 for (
auto i :
xrange(*r)) {
162 e = hexEditors.insert(e, std::make_unique<DebuggableEditor>(
manager, debuggableName, index + i));
171 auto createHexEditor = [&](
const std::string& name) {
174 for (
auto it = b; it != e; ++it) {
181 auto index = std::distance(b, e);
182 auto it = hexEditors.insert(e, std::make_unique<DebuggableEditor>(
manager, name, index));
186 im::Menu(
"Debugger", motherBoard !=
nullptr, [&]{
187 ImGui::MenuItem(
"Tool bar",
nullptr, &showControl);
188 if (ImGui::MenuItem(
"Disassembly ...")) {
189 openOrCreate(
manager, disassemblyViewers);
191 ImGui::MenuItem(
"CPU registers",
nullptr, &showRegisters);
192 ImGui::MenuItem(
"CPU flags",
nullptr, &showFlags);
193 ImGui::MenuItem(
"Slots",
nullptr, &showSlots);
194 ImGui::MenuItem(
"Stack",
nullptr, &showStack);
196 bool memoryOpen = (it != hexEditors.end()) && (*it)->open;
197 if (ImGui::MenuItem(
"Memory",
nullptr, &memoryOpen)) {
199 createHexEditor(
"memory");
201 assert(it != hexEditors.end());
207 ImGui::MenuItem(
"Symbol manager",
nullptr, &
manager.
symbols->show);
210 if (ImGui::MenuItem(
"VDP bitmap viewer ...")) {
211 openOrCreate(
manager, bitmapViewers);
213 if (ImGui::MenuItem(
"VDP tile viewer ...")) {
214 openOrCreate(
manager, tileViewers);
216 if (ImGui::MenuItem(
"VDP sprite viewer ...")) {
217 openOrCreate(
manager, spriteViewers);
219 ImGui::MenuItem(
"VDP register viewer",
nullptr, &
manager.
vdpRegs->show);
220 ImGui::MenuItem(
"Palette editor",
nullptr, &
manager.
palette->window.open);
224 auto debuggables = to_vector<std::pair<std::string, Debuggable*>>(debugger.getDebuggables());
226 for (
const auto& [name, debuggable] : debuggables) {
227 if (ImGui::Selectable(
strCat(name,
" ...").c_str())) {
228 createHexEditor(name);
237 if (disassemblyViewers.empty()) {
238 openOrCreate(
manager, disassemblyViewers);
240 disassemblyViewers.front()->setGotoTarget(target);
245 if (!motherBoard)
return;
251 drawControl(cpuInterface, *motherBoard);
252 drawSlots(cpuInterface, debugger);
253 drawStack(regs, cpuInterface, time);
266void ImGuiDebugger::actionStepIn(MSXCPUInterface& cpuInterface)
268 cpuInterface.doStep();
270void ImGuiDebugger::actionStepOver()
274void ImGuiDebugger::actionStepOut()
278void ImGuiDebugger::actionStepBack()
281 for (
auto& d : disassemblyViewers) d->syncWithPC();
290 if (shortcuts.checkShortcut(DEBUGGER_BREAK_CONTINUE)) {
291 actionBreakContinue(cpuInterface);
292 }
else if (shortcuts.checkShortcut(DEBUGGER_STEP_IN)) {
293 actionStepIn(cpuInterface);
294 }
else if (shortcuts.checkShortcut(DEBUGGER_STEP_OVER)) {
296 }
else if (shortcuts.checkShortcut(DEBUGGER_STEP_OUT)) {
298 }
else if (shortcuts.checkShortcut(DEBUGGER_STEP_BACK)) {
300 }
else if (shortcuts.checkShortcut(DISASM_TOGGLE_BREAKPOINT)) {
307 if (!showControl)
return;
308 im::Window(
"Debugger tool bar", &showControl, [&]{
312 auto* font = ImGui::GetFont();
314 DEBUGGER_ICON_RUN, DEBUGGER_ICON_BREAK,
315 DEBUGGER_ICON_STEP_IN, DEBUGGER_ICON_STEP_OVER,
316 DEBUGGER_ICON_STEP_OUT, DEBUGGER_ICON_STEP_BACK,
318 const auto*
g = font->FindGlyph(c);
319 maxIconSize = max(maxIconSize,
gl::vec2{
g->X1 -
g->X0,
g->Y1 -
g->Y0});
322 auto ButtonGlyph = [&](
const char*
id, ImWchar glyph,
Shortcuts::ID sid) {
327 if (shortcut.keyChord == ImGuiKey_None)
return id;
335 if (
auto breakContinueIcon = breaked ? DEBUGGER_ICON_RUN : DEBUGGER_ICON_BREAK;
336 ButtonGlyph(
"run", breakContinueIcon, DEBUGGER_BREAK_CONTINUE)) {
337 actionBreakContinue(cpuInterface);
339 const auto& style = ImGui::GetStyle();
340 ImGui::SameLine(0.0f, 2.0f * style.ItemSpacing.x);
343 if (ButtonGlyph(
"step-in", DEBUGGER_ICON_STEP_IN, DEBUGGER_STEP_IN)) {
344 actionStepIn(cpuInterface);
348 if (ButtonGlyph(
"step-over", DEBUGGER_ICON_STEP_OVER, DEBUGGER_STEP_OVER)) {
353 if (ButtonGlyph(
"step-out", DEBUGGER_ICON_STEP_OUT, DEBUGGER_STEP_OUT)) {
358 if (ButtonGlyph(
"step-back", DEBUGGER_ICON_STEP_BACK, DEBUGGER_STEP_BACK)) {
365void ImGuiDebugger::drawSlots(MSXCPUInterface& cpuInterface, Debugger& debugger)
367 if (!showSlots)
return;
369 int flags = ImGuiTableFlags_BordersInnerV |
370 ImGuiTableFlags_Resizable |
371 ImGuiTableFlags_Reorderable |
372 ImGuiTableFlags_Hideable |
373 ImGuiTableFlags_ContextMenuInBody;
375 ImGui::TableSetupColumn(
"Page");
376 ImGui::TableSetupColumn(
"Address", ImGuiTableColumnFlags_DefaultHide);
377 ImGui::TableSetupColumn(
"Slot");
378 ImGui::TableSetupColumn(
"Segment");
379 ImGui::TableHeadersRow();
381 for (
auto page :
xrange(uint8_t(4))) {
382 auto addr = 0x4000 * page;
383 if (ImGui::TableNextColumn()) {
386 if (ImGui::TableNextColumn()) {
389 if (ImGui::TableNextColumn()) {
390 int ps = cpuInterface.getPrimarySlot(page);
391 if (cpuInterface.isExpanded(ps)) {
392 int ss = cpuInterface.getSecondarySlot(page);
398 if (ImGui::TableNextColumn()) {
399 const auto* device = cpuInterface.getVisibleMSXDevice(page);
405 char separator =
'R';
406 for (
int offset = 0; offset < 0x4000; offset += blockSize) {
408 if (
auto seg = romBlocks->readExt(addr + offset); seg != unsigned(-1)) {
428void ImGuiDebugger::drawStack(
const CPURegs& regs,
const MSXCPUInterface& cpuInterface, EmuTime::param time)
430 if (!showStack)
return;
432 auto line = ImGui::GetTextLineHeightWithSpacing();
433 ImGui::SetNextWindowSize(ImVec2(0.0f, 12 * line), ImGuiCond_FirstUseEver);
435 auto sp = regs.getSP();
437 int flags = ImGuiTableFlags_ScrollY |
438 ImGuiTableFlags_BordersInnerV |
439 ImGuiTableFlags_Resizable |
440 ImGuiTableFlags_Reorderable |
441 ImGuiTableFlags_Hideable |
442 ImGuiTableFlags_ContextMenuInBody;
444 ImGui::TableSetupScrollFreeze(0, 1);
445 ImGui::TableSetupColumn(
"Address");
446 ImGui::TableSetupColumn(
"Offset", ImGuiTableColumnFlags_DefaultHide);
447 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_NoHide);
448 ImGui::TableHeadersRow();
452 auto offset = 2 * row;
453 auto addr = sp + offset;
454 if (ImGui::TableNextColumn()) {
457 if (ImGui::TableNextColumn()) {
458 ImGui::Text(
"SP+%X", offset);
460 if (ImGui::TableNextColumn()) {
461 auto l = cpuInterface.peekMem(narrow_cast<uint16_t>(addr + 0), time);
462 auto h = cpuInterface.peekMem(narrow_cast<uint16_t>(addr + 1), time);
463 auto value = narrow<uint16_t>(256 * h + l);
471void ImGuiDebugger::drawRegisters(CPURegs& regs)
473 if (!showRegisters)
return;
474 im::Window(
"CPU registers", &showRegisters, [&]{
477 const auto& style = ImGui::GetStyle();
478 auto padding = 2 * style.FramePadding.x;
480 auto edit16 = [&](std::string_view label, std::string_view high, std::string_view low,
auto getter,
auto setter) {
481 uint16_t value = getter();
483 ImGui::AlignTextToFramePadding();
486 ImGui::SetNextItemWidth(width16);
487 if (ImGui::InputScalar(
tmpStrCat(
"##", label).c_str(), ImGuiDataType_U16, &value,
nullptr,
nullptr,
"%04X")) {
493 "Bin: ", bin_string<4>(value >> 12),
' ', bin_string<4>(value >> 8),
' ',
494 bin_string<4>(value >> 4),
' ', bin_string<4>(value >> 0),
"\n"
495 "Dec: ", dec_string<5>(value),
'\n',
496 high,
": ", dec_string<3>(value >> 8),
" ", low,
": ", dec_string<3>(value & 0xff));
499 auto edit8 = [&](std::string_view label,
auto getter,
auto setter) {
500 uint8_t value = getter();
502 ImGui::AlignTextToFramePadding();
505 ImGui::SetNextItemWidth(width16);
506 if (ImGui::InputScalar(
tmpStrCat(
"##", label).c_str(), ImGuiDataType_U8, &value,
nullptr,
nullptr,
"%02X")) {
512 "Bin: ", bin_string<4>(value >> 4),
' ', bin_string<4>(value >> 0),
"\n"
513 "Dec: ", dec_string<3>(value),
'\n');
517 edit16(
"AF",
"A",
"F", [&]{
return regs.getAF(); }, [&](uint16_t value) { regs.setAF(value); });
518 ImGui::SameLine(0.0f, 20.0f);
519 edit16(
"AF'",
"A'",
"F'", [&]{
return regs.getAF2(); }, [&](uint16_t value) { regs.setAF2(value); });
521 edit16(
"BC",
"B",
"C", [&]{
return regs.getBC(); }, [&](uint16_t value) { regs.setBC(value); });
522 ImGui::SameLine(0.0f, 20.0f);
523 edit16(
"BC'",
"B'",
"C'", [&]{
return regs.getBC2(); }, [&](uint16_t value) { regs.setBC2(value); });
525 edit16(
"DE",
"D",
"E", [&]{
return regs.getDE(); }, [&](uint16_t value) { regs.setDE(value); });
526 ImGui::SameLine(0.0f, 20.0f);
527 edit16(
"DE'",
"D'",
"E'", [&]{
return regs.getDE2(); }, [&](uint16_t value) { regs.setDE2(value); });
529 edit16(
"HL",
"H",
"L", [&]{
return regs.getHL(); }, [&](uint16_t value) { regs.setHL(value); });
530 ImGui::SameLine(0.0f, 20.0f);
531 edit16(
"HL'",
"H'",
"L'", [&]{
return regs.getHL2(); }, [&](uint16_t value) { regs.setHL2(value); });
533 edit16(
"IX",
"IXh",
"IXl", [&]{
return regs.getIX(); }, [&](uint16_t value) { regs.setIX(value); });
534 ImGui::SameLine(0.0f, 20.0f);
535 edit16(
"IY ",
"IYh",
"IYl", [&]{
return regs.getIY(); }, [&](uint16_t value) { regs.setIY(value); });
537 edit16(
"PC",
"PCh",
"PCl", [&]{
return regs.getPC(); }, [&](uint16_t value) { regs.setPC(value); });
538 ImGui::SameLine(0.0f, 20.0f);
539 edit16(
"SP ",
"SPh",
"SPl", [&]{
return regs.getSP(); }, [&](uint16_t value) { regs.setSP(value); });
541 edit8(
"I ", [&]{
return regs.getI(); }, [&](uint8_t value) { regs.setI(value); });
542 ImGui::SameLine(0.0f, 20.0f);
543 edit8(
"R ", [&]{
return regs.getR(); }, [&](uint8_t value) { regs.setR(value); });
545 ImGui::AlignTextToFramePadding();
548 ImGui::SetNextItemWidth(width16);
549 if (uint8_t
im = regs.getIM();
550 ImGui::InputScalar(
"##IM", ImGuiDataType_U8, &
im,
nullptr,
nullptr,
"%d")) {
551 if (
im <= 2) regs.setIM(
im);
554 ImGui::SameLine(0.0f, 20.0f);
555 ImGui::AlignTextToFramePadding();
556 if (
bool ei = regs.getIFF1();
557 ImGui::Selectable(ei ?
"EI" :
"DI", false, ImGuiSelectableFlags_AllowDoubleClick)) {
558 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
567void ImGuiDebugger::drawFlags(CPURegs& regs)
569 if (!showFlags)
return;
571 auto [sizeH1_, sizeH2_, sizeV_] = [&]{
580 auto sizeH1 = sizeH1_;
auto sizeH2 = sizeH2_;
auto sizeV = sizeV_;
582 auto f = regs.getF();
584 auto draw = [&](
const char* name, uint8_t bit,
const char* val0 =
nullptr,
const char* val1 =
nullptr) {
587 if (flagsLayout == 0) {
590 s = (f & bit) ? val1 : val0;
593 s =
strCat(name,
':', (f & bit) ?
'1' :
'0');
598 s =
strCat(name,
' ', (f & bit) ?
'1' :
'0');
600 strAppend(s,
" (", (f & bit) ? val1 : val0,
')');
605 if (ImGui::Selectable(s.c_str(), false, ImGuiSelectableFlags_AllowDoubleClick, sz)) {
606 if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
611 simpleToolTip(
"double-click to toggle, right-click to configure");
612 if (flagsLayout == 0) {
614 ImGui::SameLine(0.0f, sizeH1.x);
618 draw(
"S", 0x80,
" P",
" M");
619 draw(
"Z", 0x40,
"NZ",
" Z");
620 if (showXYFlags) draw(
"Y", 0x20);
622 if (showXYFlags) draw(
"X", 0x08);
623 draw(
"P", 0x04,
"PO",
"PE");
625 draw(
"C", 0x01,
"NC",
" C");
629 ImGui::RadioButton(
"horizontal", &flagsLayout, 0);
630 ImGui::RadioButton(
"vertical", &flagsLayout, 1);
632 ImGui::Checkbox(
"show undocumented", &showXYFlags);
std::string_view getDebuggableName() const
void loadStart() override
void showMenu(MSXMotherBoard *motherBoard) override
void checkShortcuts(MSXCPUInterface &cpuInterface, MSXMotherBoard &motherBoard)
ImGuiDebugger(ImGuiManager &manager)
void loadLine(std::string_view name, zstring_view value) override
void setGotoTarget(uint16_t target)
void paint(MSXMotherBoard *motherBoard) override
void save(ImGuiTextBuffer &buf) override
static std::pair< const MSXRom *, RomBlockDebuggableBase * > getRomBlocks(Debugger &debugger, const MSXDevice *device)
static void actionToggleBp(MSXMotherBoard &motherBoard)
std::unique_ptr< ImGuiBreakPoints > breakPoints
std::unique_ptr< ImGuiVdpRegs > vdpRegs
std::unique_ptr< ImGuiWatchExpr > watchExpr
std::unique_ptr< ImGuiPalette > palette
Shortcuts & getShortcuts()
void executeDelayed(std::function< void()> action)
std::unique_ptr< ImGuiSymbols > symbols
EmuTime::param getCurrentTime() const
Convenience method: This is the same as getScheduler().getCurrentTime().
MSXCPUInterface & getCPUInterface()
static unsigned getBlockSize(RomType type)
const Shortcut & getShortcut(ID id) const
Like std::string_view, but with the extra guarantee that it refers to a zero-terminated string.
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 PopupContextWindow(const char *str_id, ImGuiPopupFlags popup_flags, std::invocable<> auto next)
bool Menu(const char *label, bool enabled, std::invocable<> auto next)
void Disabled(bool b, std::invocable<> auto next)
void Group(std::invocable<> auto next)
void Font(ImFont *font, std::invocable<> auto next)
void ListClipper(size_t count, int forceIndex, float lineHeight, 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 simpleToolTip(std::string_view desc)
void savePersistent(ImGuiTextBuffer &buf, C &c, const std::tuple< Elements... > &tup)
std::string getKeyChordName(ImGuiKeyChord keyChord)
bool ButtonWithCenteredGlyph(ImWchar glyph, gl::vec2 maxGlyphSize)
auto find(InputRange &&range, const T &value)
auto equal_range(ForwardRange &&range, const T &value, Compare comp={})
constexpr void sort(RandomAccessRange &&range)
auto lower_bound(ForwardRange &&range, const T &value, Compare comp={}, Proj proj={})
TemporaryString tmpStrCat(Ts &&... ts)
void strAppend(std::string &result, Ts &&...ts)
constexpr void repeat(T n, Op op)
Repeat the given operation 'op' 'n' times.
constexpr auto xrange(T e)