This commit is contained in:
aurora 2022-08-29 05:44:34 +03:00
commit 3b0df75bf6
112 changed files with 8512 additions and 634 deletions

View file

@ -102,6 +102,7 @@ const char* aboutLine[]={
"fd",
"GENATARi",
"host12prog",
"lunathir",
"plane",
"TheEssem",
"",
@ -133,6 +134,8 @@ const char* aboutLine[]={
"puNES (NES, MMC5 and FDS) by FHorse",
"NSFPlay (NES and FDS) by Brad Smith and Brezza",
"reSID by Dag Lem",
"reSIDfp by Dag Lem, Antti Lankila",
"and Leandro Nini",
"Stella by Stella Team",
"QSound emulator by superctr and Valley Bell",
"VICE VIC-20 sound core by Rami Rasanen and viznut",

View file

@ -616,9 +616,9 @@ void FurnaceGUI::doInterpolate() {
}
} else {
for (int j=selStart.y; j<=selEnd.y; j++) {
if (pat->data[j][0]!=0 && pat->data[j][1]!=0) {
if (pat->data[j][0]!=0 || pat->data[j][1]!=0) {
if (pat->data[j][0]!=100 && pat->data[j][0]!=101 && pat->data[j][0]!=102) {
points.emplace(points.end(),j,pat->data[j][0]+pat->data[j][1]*12);
points.emplace(points.end(),j,pat->data[j][0]+(signed char)pat->data[j][1]*12);
}
}
}

View file

@ -195,23 +195,30 @@ void FurnaceGUI::decodeKeyMap(std::map<int,int>& map, String source) {
}
}
void FurnaceGUI::encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex) {
void FurnaceGUI::encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex, bool bit30) {
target="";
char buf[32];
for (int i=0; i<macroLen; i++) {
if (i==macroLoop) target+="| ";
if (i==macroRel) target+="/ ";
if (bit30 && ((macro[i]&0xc0000000)==0x40000000 || (macro[i]&0xc0000000)==0x80000000)) target+="@";
int macroVal=macro[i];
if (macro[i]<0) {
if (!(macroVal&0x40000000)) macroVal|=0x40000000;
} else {
if (macroVal&0x40000000) macroVal&=~0x40000000;
}
if (hex) {
if (i==macroLen-1) {
snprintf(buf,31,"%.2X",macro[i]);
snprintf(buf,31,"%.2X",macroVal);
} else {
snprintf(buf,31,"%.2X ",macro[i]);
snprintf(buf,31,"%.2X ",macroVal);
}
} else {
if (i==macroLen-1) {
snprintf(buf,31,"%d",macro[i]);
snprintf(buf,31,"%d",macroVal);
} else {
snprintf(buf,31,"%d ",macro[i]);
snprintf(buf,31,"%d ",macroVal);
}
}
target+=buf;
@ -276,9 +283,10 @@ void FurnaceGUI::decodeMMLStrW(String& source, int* macro, int& macroLen, int ma
}
}
void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel) {
void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel, bool bit30) {
int buf=0;
bool negaBuf=false;
bool setBit30=false;
bool hasVal=false;
macroLen=0;
macroLoop=255;
@ -297,6 +305,11 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=true;
}
break;
case '@':
if (bit30) {
setBit30=true;
}
break;
case ' ':
if (hasVal) {
hasVal=false;
@ -304,6 +317,8 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
@ -315,6 +330,8 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
@ -329,6 +346,8 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
@ -345,6 +364,8 @@ void FurnaceGUI::decodeMMLStr(String& source, int* macro, unsigned char& macroLe
negaBuf=false;
if (macro[macroLen]<macroMin) macro[macroLen]=macroMin;
if (macro[macroLen]>macroMax) macro[macroLen]=macroMax;
if (setBit30) macro[macroLen]^=0x40000000;
setBit30=false;
macroLen++;
buf=0;
}
@ -1572,6 +1593,7 @@ void FurnaceGUI::openFileDialog(FurnaceGUIFileDialogs type) {
int FurnaceGUI::save(String path, int dmfVersion) {
SafeWriter* w;
if (dmfVersion) {
if (dmfVersion<24) dmfVersion=24;
w=e->saveDMF(dmfVersion);
} else {
w=e->saveFur();
@ -1758,8 +1780,35 @@ void FurnaceGUI::showError(String what) {
displayError=true;
}
// what monster did I just create here?
#define B30(tt) (macroDragBit30?((((tt)&0xc0000000)==0x40000000 || ((tt)&0xc0000000)==0x80000000)?0x40000000:0):0)
#define MACRO_DRAG(t) \
if (macroDragBitMode) { \
if (macroDragSettingBit30) { \
if (macroDragLastX!=x || macroDragLastY!=y) { \
macroDragLastX=x; \
macroDragLastY=y; \
if (macroDragInitialValueSet) { \
if (!macroDragInitialValue) { \
if (t[x]&0x80000000) { \
t[x]&=~0x40000000; \
} else { \
t[x]|=0x40000000; \
} \
} else { \
if (t[x]&0x80000000) { \
t[x]|=0x40000000; \
} else { \
t[x]&=~0x40000000; \
} \
} \
} else { \
macroDragInitialValue=(((t[x])&0xc0000000)==0x40000000 || ((t[x])&0xc0000000)==0x80000000); \
macroDragInitialValueSet=true; \
t[x]^=0x40000000; \
} \
} \
} else if (macroDragBitMode) { \
if (macroDragLastX!=x || macroDragLastY!=y) { \
macroDragLastX=x; \
macroDragLastY=y; \
@ -1790,25 +1839,25 @@ void FurnaceGUI::showError(String what) {
} \
if (macroDragMouseMoved) { \
if ((int)round(x-macroDragLineInitial.x)==0) { \
t[x]=macroDragLineInitial.y; \
t[x]=B30(t[x])^(int)(macroDragLineInitial.y); \
} else { \
if ((int)round(x-macroDragLineInitial.x)<0) { \
for (int i=0; i<=(int)round(macroDragLineInitial.x-x); i++) { \
int index=(int)round(x+i); \
if (index<0) continue; \
t[index]=y+(macroDragLineInitial.y-y)*((float)i/(float)(macroDragLineInitial.x-x)); \
t[index]=B30(t[index])^(int)(y+(macroDragLineInitial.y-y)*((float)i/(float)(macroDragLineInitial.x-x))); \
} \
} else { \
for (int i=0; i<=(int)round(x-macroDragLineInitial.x); i++) { \
int index=(int)round(i+macroDragLineInitial.x); \
if (index<0) continue; \
t[index]=macroDragLineInitial.y+(y-macroDragLineInitial.y)*((float)i/(x-macroDragLineInitial.x)); \
t[index]=B30(t[index])^(int)(macroDragLineInitial.y+(y-macroDragLineInitial.y)*((float)i/(x-macroDragLineInitial.x))); \
} \
} \
} \
} \
} else { \
t[x]=y; \
t[x]=B30(t[x])^(y); \
} \
}
@ -1893,20 +1942,6 @@ void FurnaceGUI::processDrags(int dragX, int dragY) {
}
}
#define sysAddOption(x) \
if (ImGui::MenuItem(getSystemName(x))) { \
if (!e->addSystem(x)) { \
showError("cannot add chip! ("+e->getLastError()+")"); \
} \
updateWindowTitle(); \
}
#define sysChangeOption(x,y) \
if (ImGui::MenuItem(getSystemName(y),NULL,e->song.system[x]==y)) { \
e->changeSystem(x,y,preserveChanPos); \
updateWindowTitle(); \
}
#define checkExtension(x) \
String lowerCase=fileName; \
for (char& i: lowerCase) { \
@ -3057,9 +3092,13 @@ bool FurnaceGUI::loop() {
}
ImGui::Separator();
if (ImGui::BeginMenu("add chip...")) {
for (int j=0; availableSystems[j]; j++) {
if (!settings.hiddenSystems && (availableSystems[j]==DIV_SYSTEM_YMU759 || availableSystems[j]==DIV_SYSTEM_DUMMY)) continue;
sysAddOption((DivSystem)availableSystems[j]);
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
if (!e->addSystem(picked)) {
showError("cannot add chip! ("+e->getLastError()+")");
}
ImGui::CloseCurrentPopup();
updateWindowTitle();
}
ImGui::EndMenu();
}
@ -3076,9 +3115,11 @@ bool FurnaceGUI::loop() {
ImGui::Checkbox("Preserve channel positions",&preserveChanPos);
for (int i=0; i<e->song.systemLen; i++) {
if (ImGui::BeginMenu(fmt::sprintf("%d. %s##_SYSC%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
for (int j=0; availableSystems[j]; j++) {
if (!settings.hiddenSystems && (availableSystems[j]==DIV_SYSTEM_YMU759 || availableSystems[j]==DIV_SYSTEM_DUMMY)) continue;
sysChangeOption(i,(DivSystem)availableSystems[j]);
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
e->changeSystem(i,picked,preserveChanPos);
updateWindowTitle();
ImGui::CloseCurrentPopup();
}
ImGui::EndMenu();
}
@ -4167,6 +4208,16 @@ bool FurnaceGUI::loop() {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_SYSTEM_DEL:
if (ImGui::Button("Yes")) {
e->removeSystem(sysToDelete,preserveChanPos);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("No")) {
ImGui::CloseCurrentPopup();
}
break;
case GUI_WARN_GENERIC:
if (ImGui::Button("OK")) {
ImGui::CloseCurrentPopup();
@ -4775,6 +4826,7 @@ FurnaceGUI::FurnaceGUI():
drawHalt(10),
macroPointSize(16),
waveEditStyle(0),
curSysSection(NULL),
pendingRawSampleDepth(8),
pendingRawSampleChannels(1),
pendingRawSampleUnsigned(false),
@ -4935,6 +4987,8 @@ FurnaceGUI::FurnaceGUI():
macroDragInitialValueSet(false),
macroDragInitialValue(false),
macroDragChar(false),
macroDragBit30(false),
macroDragSettingBit30(false),
macroDragLineMode(false),
macroDragMouseMoved(false),
macroDragLineInitial(0,0),
@ -4973,6 +5027,8 @@ FurnaceGUI::FurnaceGUI():
eventTimeEnd(0),
eventTimeDelta(0),
chanToMove(-1),
sysToMove(-1),
sysToDelete(-1),
transposeAmount(0),
randomizeMin(0),
randomizeMax(255),

View file

@ -310,6 +310,7 @@ enum FurnaceGUIWarnings {
GUI_WARN_CLOSE_SETTINGS,
GUI_WARN_CLEAR,
GUI_WARN_SUBSONG_DEL,
GUI_WARN_SYSTEM_DEL,
GUI_WARN_GENERIC
};
@ -869,10 +870,10 @@ struct FurnaceGUIMacroDesc {
const char* modeName;
ImVec4 color;
unsigned int bitOffset;
bool isBitfield, blockMode;
bool isBitfield, blockMode, bit30;
String (*hoverFunc)(int,float);
FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0):
FurnaceGUIMacroDesc(const char* name, DivInstrumentMacro* m, int macroMin, int macroMax, float macroHeight, ImVec4 col=ImVec4(1.0f,1.0f,1.0f,1.0f), bool block=false, const char* mName=NULL, String (*hf)(int,float)=NULL, bool bitfield=false, const char** bfVal=NULL, unsigned int bitOff=0, bool bit30Special=false):
macro(m),
height(macroHeight),
displayName(name),
@ -882,6 +883,7 @@ struct FurnaceGUIMacroDesc {
bitOffset(bitOff),
isBitfield(bitfield),
blockMode(block),
bit30(bit30Special),
hoverFunc(hf) {
// MSVC -> hell
this->min=macroMin;
@ -967,13 +969,16 @@ class FurnaceGUI {
int sampleTexW, sampleTexH;
bool updateSampleTex;
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile;
String workingDir, fileName, clipboard, warnString, errorString, lastError, curFileName, nextFile, sysSearchQuery, newSongQuery;
String workingDirSong, workingDirIns, workingDirWave, workingDirSample, workingDirAudioExport;
String workingDirVGMExport, workingDirROMExport, workingDirFont, workingDirColors, workingDirKeybinds;
String workingDirLayout, workingDirROM, workingDirTest;
String mmlString[32];
String mmlStringW;
std::vector<DivSystem> sysSearchResults;
std::vector<FurnaceGUISysDef> newSongSearchResults;
bool quit, warnQuit, willCommit, edit, modified, displayError, displayExporting, vgmExportLoop, vgmExportPatternHints;
bool wantCaptureKeyboard, oldWantCaptureKeyboard, displayMacroMenu;
bool displayNew, fullScreen, preserveChanPos, wantScrollList, noteInputPoly;
@ -983,6 +988,7 @@ class FurnaceGUI {
int drawHalt;
int macroPointSize;
int waveEditStyle;
const int* curSysSection;
String pendingRawSample;
int pendingRawSampleDepth, pendingRawSampleChannels;
@ -1045,6 +1051,7 @@ class FurnaceGUI {
int snCore;
int nesCore;
int fdsCore;
int c64Core;
int pcSpeakerOutMethod;
String yrw801Path;
String tg100Path;
@ -1087,6 +1094,8 @@ class FurnaceGUI {
int roundedMenus;
int loadJapanese;
int loadChinese;
int loadChineseTraditional;
int loadKorean;
int fmLayout;
int sampleLayout;
int waveLayout;
@ -1163,6 +1172,7 @@ class FurnaceGUI {
snCore(0),
nesCore(0),
fdsCore(0),
c64Core(1),
pcSpeakerOutMethod(0),
yrw801Path(""),
tg100Path(""),
@ -1205,6 +1215,8 @@ class FurnaceGUI {
roundedMenus(0),
loadJapanese(0),
loadChinese(0),
loadChineseTraditional(0),
loadKorean(0),
fmLayout(0),
sampleLayout(0),
waveLayout(0),
@ -1398,6 +1410,8 @@ class FurnaceGUI {
bool macroDragInitialValueSet;
bool macroDragInitialValue;
bool macroDragChar;
bool macroDragBit30;
bool macroDragSettingBit30;
bool macroDragLineMode;
bool macroDragMouseMoved;
ImVec2 macroDragLineInitial;
@ -1429,7 +1443,7 @@ class FurnaceGUI {
int renderTimeBegin, renderTimeEnd, renderTimeDelta;
int eventTimeBegin, eventTimeEnd, eventTimeDelta;
int chanToMove;
int chanToMove, sysToMove, sysToDelete;
ImVec2 patWindowPos, patWindowSize;
@ -1661,6 +1675,7 @@ class FurnaceGUI {
void doReplace();
void doDrag();
void editOptions(bool topMenu);
DivSystem systemPicker();
void noteInput(int num, int key, int vol=-1);
void valueInput(int num, bool direct=false, int target=-1);
@ -1689,8 +1704,8 @@ class FurnaceGUI {
void applyUISettings(bool updateFonts=true);
void initSystemPresets();
void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex=false);
void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel);
void encodeMMLStr(String& target, int* macro, int macroLen, int macroLoop, int macroRel, bool hex=false, bool bit30=false);
void decodeMMLStr(String& source, int* macro, unsigned char& macroLen, unsigned char& macroLoop, int macroMin, int macroMax, unsigned char& macroRel, bool bit30=false);
void decodeMMLStrW(String& source, int* macro, int& macroLen, int macroMax, bool hex=false);
String encodeKeyMap(std::map<int,int>& map);

View file

@ -839,7 +839,9 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
};
#undef D
// define systems.
// define chips here
// all chips
const int availableSystems[]={
DIV_SYSTEM_YM2612,
DIV_SYSTEM_YM2612_EXT,
@ -911,3 +913,115 @@ const int availableSystems[]={
0 // don't remove this last one!
};
// FM
const int chipsFM[]={
DIV_SYSTEM_YM2612,
DIV_SYSTEM_YM2612_EXT,
DIV_SYSTEM_YM2612_FRAC,
DIV_SYSTEM_YM2612_FRAC_EXT,
DIV_SYSTEM_YM2151,
DIV_SYSTEM_YM2610,
DIV_SYSTEM_YM2610_EXT,
DIV_SYSTEM_YM2610_FULL,
DIV_SYSTEM_YM2610_FULL_EXT,
DIV_SYSTEM_YM2610B,
DIV_SYSTEM_YM2610B_EXT,
DIV_SYSTEM_YMU759,
DIV_SYSTEM_OPN,
DIV_SYSTEM_OPN_EXT,
DIV_SYSTEM_PC98,
DIV_SYSTEM_PC98_EXT,
DIV_SYSTEM_OPLL,
DIV_SYSTEM_OPLL_DRUMS,
DIV_SYSTEM_VRC7,
DIV_SYSTEM_OPL,
DIV_SYSTEM_OPL_DRUMS,
DIV_SYSTEM_Y8950,
DIV_SYSTEM_Y8950_DRUMS,
DIV_SYSTEM_OPL2,
DIV_SYSTEM_OPL2_DRUMS,
DIV_SYSTEM_OPL3,
DIV_SYSTEM_OPL3_DRUMS,
DIV_SYSTEM_OPZ,
0 // don't remove this last one!
};
// square
const int chipsSquare[]={
DIV_SYSTEM_SMS,
DIV_SYSTEM_AY8910,
DIV_SYSTEM_PCSPKR,
DIV_SYSTEM_SAA1099,
DIV_SYSTEM_VIC20,
0 // don't remove this last one!
};
// wavetable
const int chipsWave[]={
DIV_SYSTEM_PCE,
DIV_SYSTEM_X1_010,
DIV_SYSTEM_SWAN,
DIV_SYSTEM_BUBSYS_WSG,
DIV_SYSTEM_N163,
DIV_SYSTEM_FDS,
DIV_SYSTEM_SCC,
DIV_SYSTEM_SCC_PLUS,
DIV_SYSTEM_NAMCO,
DIV_SYSTEM_NAMCO_15XX,
DIV_SYSTEM_NAMCO_CUS30,
0 // don't remove this last one!
};
// specialized
const int chipsSpecial[]={
DIV_SYSTEM_GB,
DIV_SYSTEM_NES,
DIV_SYSTEM_C64_8580,
DIV_SYSTEM_C64_6581,
DIV_SYSTEM_SFX_BEEPER,
DIV_SYSTEM_DUMMY,
DIV_SYSTEM_SOUND_UNIT,
DIV_SYSTEM_TIA,
DIV_SYSTEM_AY8930,
DIV_SYSTEM_LYNX,
DIV_SYSTEM_VERA,
DIV_SYSTEM_PET,
DIV_SYSTEM_VRC6,
DIV_SYSTEM_MMC5,
0 // don't remove this last one!
};
// sample
const int chipsSample[]={
DIV_SYSTEM_SEGAPCM,
DIV_SYSTEM_SEGAPCM_COMPAT,
DIV_SYSTEM_AMIGA,
DIV_SYSTEM_QSOUND,
DIV_SYSTEM_X1_010,
DIV_SYSTEM_YMZ280B,
DIV_SYSTEM_MSM6258,
DIV_SYSTEM_MSM6295,
DIV_SYSTEM_RF5C68,
DIV_SYSTEM_PCM_DAC,
0 // don't remove this last one!
};
const int* chipCategories[]={
availableSystems,
chipsFM,
chipsSquare,
chipsWave,
chipsSpecial,
chipsSample,
NULL
};
const char* chipCategoryNames[]={
"All chips",
"FM",
"Square",
"Wavetable",
"Special",
"Sample",
NULL
};

View file

@ -42,7 +42,14 @@ extern const char* pitchLabel[11];
extern const char* insTypes[];
extern const char* sampleDepths[];
extern const char* resampleStrats[];
extern const char* chipCategoryNames[];
extern const int availableSystems[];
extern const int chipsFM[];
extern const int chipsSquare[];
extern const int chipsWavetable[];
extern const int chipsSpecial[];
extern const int chipsSample[];
extern const int* chipCategories[];
extern const FurnaceGUIActionDef guiActions[];
extern const FurnaceGUIColorDef guiColors[];
extern const int altValues[24];

View file

@ -331,6 +331,11 @@ String macroHoverLoop(int id, float val) {
return "";
}
String macroHoverBit30(int id, float val) {
if (val>0) return "Fixed";
return "Relative";
}
String macroHoverES5506FilterMode(int id, float val) {
String mode="???";
switch (((int)val)&3) {
@ -1192,10 +1197,21 @@ String genericGuide(float value) {
return fmt::sprintf("%d",(int)value);
}
inline int deBit30(const int val) {
if ((val&0xc0000000)==0x40000000 || (val&0xc0000000)==0x80000000) return val^0x40000000;
return val;
}
inline bool enBit30(const int val) {
if ((val&0xc0000000)==0x40000000 || (val&0xc0000000)==0x80000000) return true;
return false;
}
void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
float asFloat[256];
int asInt[256];
float loopIndicator[256];
float bit30Indicator[256];
bool doHighlight[256];
int index=0;
@ -1281,12 +1297,14 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
// macro area
ImGui::TableNextColumn();
for (int j=0; j<256; j++) {
bit30Indicator[j]=0;
if (j+macroDragScroll>=i.macro->len) {
asFloat[j]=0;
asInt[j]=0;
} else {
asFloat[j]=i.macro->val[j+macroDragScroll];
asInt[j]=i.macro->val[j+macroDragScroll]+i.bitOffset;
asFloat[j]=deBit30(i.macro->val[j+macroDragScroll]);
asInt[j]=deBit30(i.macro->val[j+macroDragScroll])+i.bitOffset;
if (i.bit30) bit30Indicator[j]=enBit30(i.macro->val[j+macroDragScroll]);
}
if (j+macroDragScroll>=i.macro->len || (j+macroDragScroll>i.macro->rel && i.macro->loop<i.macro->rel)) {
loopIndicator[j]=0;
@ -1355,6 +1373,8 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
macroDragInitialValue=false;
macroDragLen=totalFit;
macroDragActive=true;
macroDragBit30=i.bit30;
macroDragSettingBit30=false;
macroDragTarget=i.macro->val;
macroDragChar=false;
macroDragLineMode=(i.isBitfield)?false:ImGui::IsItemClicked(ImGuiMouseButton_Right);
@ -1422,6 +1442,27 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
}
}
// bit 30 area
if (i.bit30) {
PlotCustom("##IMacroBit30",bit30Indicator,totalFit,macroDragScroll,NULL,0,1,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,&macroHoverBit30);
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
macroDragStart=ImGui::GetItemRectMin();
macroDragAreaSize=ImVec2(availableWidth,12.0f*dpiScale);
macroDragInitialValueSet=false;
macroDragInitialValue=false;
macroDragLen=totalFit;
macroDragActive=true;
macroDragBit30=i.bit30;
macroDragSettingBit30=true;
macroDragTarget=i.macro->val;
macroDragChar=false;
macroDragLineMode=false;
macroDragLineInitial=ImVec2(0,0);
lastMacroDesc=i;
processDrags(ImGui::GetMousePos().x,ImGui::GetMousePos().y);
}
}
// loop area
PlotCustom("##IMacroLoop",loopIndicator,totalFit,macroDragScroll,NULL,0,2,ImVec2(availableWidth,12.0f*dpiScale),sizeof(float),i.color,i.macro->len-macroDragScroll,&macroHoverLoop);
if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
@ -1446,10 +1487,10 @@ void FurnaceGUI::drawMacros(std::vector<FurnaceGUIMacroDesc>& macros) {
ImGui::SetNextItemWidth(availableWidth);
String& mmlStr=mmlString[index];
if (ImGui::InputText("##IMacroMML",&mmlStr)) {
decodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.min,(i.isBitfield)?((1<<(i.isBitfield?i.max:0))-1):i.max,i.macro->rel);
decodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.min,(i.isBitfield)?((1<<(i.isBitfield?i.max:0))-1):i.max,i.macro->rel,i.bit30);
}
if (!ImGui::IsItemActive()) {
encodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.macro->rel);
encodeMMLStr(mmlStr,i.macro->val,i.macro->len,i.macro->loop,i.macro->rel,false,i.bit30);
}
}
ImGui::PopStyleVar();
@ -4178,7 +4219,7 @@ void FurnaceGUI::drawInsEdit() {
if (volMax>0) {
macroList.push_back(FurnaceGUIMacroDesc(volumeLabel,&ins->std.volMacro,volMin,volMax,160,uiColors[GUI_COLOR_MACRO_VOLUME]));
}
macroList.push_back(FurnaceGUIMacroDesc("Arpeggio",&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,macroAbsoluteMode,ins->std.arpMacro.mode?(&macroHoverNote):NULL));
macroList.push_back(FurnaceGUIMacroDesc("Arpeggio",&ins->std.arpMacro,-120,120,160,uiColors[GUI_COLOR_MACRO_PITCH],true,NULL,NULL,false,NULL,0,true));
if (dutyMax>0) {
if (ins->type==DIV_INS_MIKEY) {
macroList.push_back(FurnaceGUIMacroDesc(dutyLabel,&ins->std.dutyMacro,0,dutyMax,160,uiColors[GUI_COLOR_MACRO_OTHER],false,NULL,NULL,true,mikeyFeedbackBits));

View file

@ -18,7 +18,8 @@
*/
#include "gui.h"
#include <imgui.h>
#include "misc/cpp/imgui_stdlib.h"
#include <algorithm>
void FurnaceGUI::drawNewSong() {
bool accepted=false;
@ -32,35 +33,76 @@ void FurnaceGUI::drawNewSong() {
avail.y-=ImGui::GetFrameHeightWithSpacing();
if (ImGui::BeginChild("sysPickerC",avail,false,ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoScrollbar)) {
if (ImGui::BeginTable("sysPicker",2,ImGuiTableFlags_BordersInnerV)) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f);
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputTextWithHint("##SysSearch","Search...",&newSongQuery)) {
String lowerCase=newSongQuery;
for (char& i: lowerCase) {
if (i>='A' && i<='Z') i+='a'-'A';
}
auto lastItem=std::remove_if(lowerCase.begin(),lowerCase.end(),[](char c) {
return (c==' ' || c=='_' || c=='-');
});
lowerCase.erase(lastItem,lowerCase.end());
newSongSearchResults.clear();
for (FurnaceGUISysCategory& i: sysCategories) {
for (FurnaceGUISysDef& j: i.systems) {
String lowerCase1=j.name;
for (char& i: lowerCase1) {
if (i>='A' && i<='Z') i+='a'-'A';
}
auto lastItem=std::remove_if(lowerCase1.begin(),lowerCase1.end(),[](char c) {
return (c==' ' || c=='_' || c=='-');
});
lowerCase1.erase(lastItem,lowerCase1.end());
if (lowerCase1.find(lowerCase)!=String::npos) {
newSongSearchResults.push_back(j);
}
}
std::sort(newSongSearchResults.begin(),newSongSearchResults.end(),[](const FurnaceGUISysDef& a, const FurnaceGUISysDef& b) {
return strcmp(a.name,b.name)<0;
});
auto lastItem=std::unique(newSongSearchResults.begin(),newSongSearchResults.end(),[](const FurnaceGUISysDef& a, const FurnaceGUISysDef& b) {
return strcmp(a.name,b.name)==0;
});
newSongSearchResults.erase(lastItem,newSongSearchResults.end());
}
}
if (ImGui::BeginTable("sysPicker",newSongQuery.empty()?2:1,ImGuiTableFlags_BordersInnerV)) {
if (newSongQuery.empty()) {
ImGui::TableSetupColumn("c0",ImGuiTableColumnFlags_WidthFixed,0.0f);
}
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthStretch,0.0f);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Categories");
ImGui::TableNextColumn();
ImGui::Text("Systems");
if (newSongQuery.empty()) {
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::Text("Categories");
ImGui::TableNextColumn();
ImGui::Text("Systems");
}
ImGui::TableNextRow();
// CATEGORIES
ImGui::TableNextColumn();
int index=0;
for (FurnaceGUISysCategory& i: sysCategories) {
if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \
newSongCategory=index;
if (newSongQuery.empty()) {
ImGui::TableNextColumn();
int index=0;
for (FurnaceGUISysCategory& i: sysCategories) {
if (ImGui::Selectable(i.name,newSongCategory==index,ImGuiSelectableFlags_DontClosePopups)) { \
newSongCategory=index;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",i.description);
}
index++;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("%s",i.description);
}
index++;
}
// SYSTEMS
ImGui::TableNextColumn();
if (ImGui::BeginTable("Systems",1,ImGuiTableFlags_BordersInnerV|ImGuiTableFlags_ScrollY)) {
for (FurnaceGUISysDef& i: sysCategories[newSongCategory].systems) {
std::vector<FurnaceGUISysDef>& category=(newSongQuery.empty())?(sysCategories[newSongCategory].systems):(newSongSearchResults);
for (FurnaceGUISysDef& i: category) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(i.name,false,ImGuiSelectableFlags_DontClosePopups)) {

View file

@ -47,21 +47,24 @@ void FurnaceGUI::drawPatManager() {
});
}
for (int i=0; i<e->getTotalChannelCount(); i++) {
memset(isUsed,0,256);
memset(isNull,0,256*sizeof(bool));
for (int j=0; j<e->curSubSong->ordersLen; j++) {
isUsed[e->curSubSong->orders.ord[i][j]]++;
}
for (int j=0; j<256; j++) {
isNull[j]=(e->curSubSong->pat[i].data[j]==NULL);
}
ImGui::Text("%d. %s",i+1,e->getChannelName(i));
ImGui::PushID(1000+i);
if (ImGui::BeginTable("PatManTable",257,ImGuiTableFlags_ScrollX|ImGuiTableFlags_SizingFixedFit)) {
ImGui::PushFont(patFont);
if (ImGui::BeginTable("PatManTable",32)) {
for (int i=0; i<e->getTotalChannelCount(); i++) {
ImGui::TableNextRow();
memset(isUsed,0,256);
memset(isNull,0,256*sizeof(bool));
for (int j=0; j<e->curSubSong->ordersLen; j++) {
isUsed[e->curSubSong->orders.ord[i][j]]++;
}
for (int j=0; j<256; j++) {
isNull[j]=(e->curSubSong->pat[i].data[j]==NULL);
}
ImGui::TableNextColumn();
ImGui::Text("%s",e->getChannelShortName(i));
ImGui::PushID(1000+i);
for (int k=0; k<256; k++) {
if ((k&31)==0) ImGui::TableNextRow();
ImGui::TableNextColumn();
snprintf(id,1023,"%.2X",k);
@ -98,10 +101,11 @@ void FurnaceGUI::drawPatManager() {
}
ImGui::PopStyleColor();
}
ImGui::EndTable();
ImGui::PopID();
}
ImGui::PopFont();
ImGui::PopID();
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_PAT_MANAGER;

View file

@ -79,7 +79,7 @@ void FurnaceGUI::initSystemPresets() {
}
));
cat.systems.push_back(FurnaceGUISysDef(
"Yamaha YM2610B (OPNB-B)", {
"Yamaha YM2610B (OPNB2)", {
DIV_SYSTEM_YM2610B, 64, 0, 0,
0
}

View file

@ -97,6 +97,11 @@ const char* nesCores[]={
"NSFplay"
};
const char* c64Cores[]={
"reSID",
"reSIDfp"
};
const char* pcspkrOutMethods[]={
"evdev SND_TONE",
"KIOCSOUND on /dev/tty1",
@ -980,6 +985,10 @@ void FurnaceGUI::drawSettings() {
ImGui::SameLine();
ImGui::Combo("##FDSCore",&settings.fdsCore,nesCores,2);
ImGui::Text("SID core");
ImGui::SameLine();
ImGui::Combo("##C64Core",&settings.c64Core,c64Cores,2);
ImGui::Separator();
ImGui::Text("PC Speaker strategy");
@ -1093,6 +1102,32 @@ void FurnaceGUI::drawSettings() {
);
}
bool loadChineseTraditionalB=settings.loadChineseTraditional;
if (ImGui::Checkbox("Display Chinese (Traditional) characters",&loadChineseTraditionalB)) {
settings.loadChineseTraditional=loadChineseTraditionalB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Only toggle this option if you have enough graphics memory.\n"
"This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n"
"請在確保你有足夠的顯存后再啟動此設定\n"
"這是一個在ImGui實現動態字體加載之前的臨時解決方案"
);
}
bool loadKoreanB=settings.loadKorean;
if (ImGui::Checkbox("Display Korean characters",&loadKoreanB)) {
settings.loadKorean=loadKoreanB;
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip(
"Only toggle this option if you have enough graphics memory.\n"
"This is a temporary solution until dynamic font atlas is implemented in Dear ImGui.\n\n"
"그래픽 메모리가 충분한 경우에만 이 옵션을 선택하십시오.\n"
"이 옵션은 Dear ImGui에 동적 글꼴 아틀라스가 구현될 때까지 임시 솔루션입니다."
);
}
ImGui::Separator();
ImGui::Text("Pattern view labels:");
@ -2137,6 +2172,7 @@ void FurnaceGUI::syncSettings() {
settings.snCore=e->getConfInt("snCore",0);
settings.nesCore=e->getConfInt("nesCore",0);
settings.fdsCore=e->getConfInt("fdsCore",0);
settings.c64Core=e->getConfInt("c64Core",1);
settings.pcSpeakerOutMethod=e->getConfInt("pcSpeakerOutMethod",0);
settings.yrw801Path=e->getConfString("yrw801Path","");
settings.tg100Path=e->getConfString("tg100Path","");
@ -2179,6 +2215,8 @@ void FurnaceGUI::syncSettings() {
settings.roundedMenus=e->getConfInt("roundedMenus",0);
settings.loadJapanese=e->getConfInt("loadJapanese",0);
settings.loadChinese=e->getConfInt("loadChinese",0);
settings.loadChineseTraditional=e->getConfInt("loadChineseTraditional",0);
settings.loadKorean=e->getConfInt("loadKorean",0);
settings.fmLayout=e->getConfInt("fmLayout",0);
settings.sampleLayout=e->getConfInt("sampleLayout",0);
settings.waveLayout=e->getConfInt("waveLayout",0);
@ -2248,6 +2286,7 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.snCore,0,1);
clampSetting(settings.nesCore,0,1);
clampSetting(settings.fdsCore,0,1);
clampSetting(settings.c64Core,0,1);
clampSetting(settings.pcSpeakerOutMethod,0,4);
clampSetting(settings.mainFont,0,6);
clampSetting(settings.patFont,0,6);
@ -2284,6 +2323,8 @@ void FurnaceGUI::syncSettings() {
clampSetting(settings.roundedMenus,0,1);
clampSetting(settings.loadJapanese,0,1);
clampSetting(settings.loadChinese,0,1);
clampSetting(settings.loadChineseTraditional,0,1);
clampSetting(settings.loadKorean,0,1);
clampSetting(settings.fmLayout,0,6);
clampSetting(settings.susPosition,0,1);
clampSetting(settings.effectCursorDir,0,2);
@ -2382,6 +2423,7 @@ void FurnaceGUI::commitSettings() {
e->setConf("snCore",settings.snCore);
e->setConf("nesCore",settings.nesCore);
e->setConf("fdsCore",settings.fdsCore);
e->setConf("c64Core",settings.c64Core);
e->setConf("pcSpeakerOutMethod",settings.pcSpeakerOutMethod);
e->setConf("yrw801Path",settings.yrw801Path);
e->setConf("tg100Path",settings.tg100Path);
@ -2424,6 +2466,8 @@ void FurnaceGUI::commitSettings() {
e->setConf("roundedMenus",settings.roundedMenus);
e->setConf("loadJapanese",settings.loadJapanese);
e->setConf("loadChinese",settings.loadChinese);
e->setConf("loadChineseTraditional",settings.loadChineseTraditional);
e->setConf("loadKorean",settings.loadKorean);
e->setConf("fmLayout",settings.fmLayout);
e->setConf("sampleLayout",settings.sampleLayout);
e->setConf("waveLayout",settings.waveLayout);
@ -3075,6 +3119,12 @@ void FurnaceGUI::applyUISettings(bool updateFonts) {
if (settings.loadChinese) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon());
}
if (settings.loadChineseTraditional) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesChineseFull());
}
if (settings.loadKorean) {
range.AddRanges(ImGui::GetIO().Fonts->GetGlyphRangesKorean());
}
// I'm terribly sorry
range.UsedChars[0x80>>5]=0;

View file

@ -20,6 +20,7 @@
#include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome4.h"
#include <fmt/printf.h>
#include <imgui.h>
void FurnaceGUI::drawSysManager() {
@ -30,7 +31,93 @@ void FurnaceGUI::drawSysManager() {
}
if (!sysManagerOpen) return;
if (ImGui::Begin("Chip Manager",&sysManagerOpen,globalWinFlags)) {
ImGui::Text("Stuff here...");
ImGui::Checkbox("Preserve channel order",&preserveChanPos);
if (ImGui::BeginTable("SystemList",3)) {
ImGui::TableSetupColumn("c1",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("c2",ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("c3",ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Text("Name");
ImGui::TableNextColumn();
ImGui::Text("Actions");
for (unsigned char i=0; i<e->song.systemLen; i++) {
ImGui::PushID(i);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Button(ICON_FA_ARROWS)) {
}
if (ImGui::BeginDragDropSource()) {
sysToMove=i;
ImGui::SetDragDropPayload("FUR_SYS",NULL,0,ImGuiCond_Once);
ImGui::Button(ICON_FA_ARROWS "##SysDrag");
ImGui::EndDragDropSource();
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("(drag to swap chips)");
}
if (ImGui::BeginDragDropTarget()) {
const ImGuiPayload* dragItem=ImGui::AcceptDragDropPayload("FUR_SYS");
if (dragItem!=NULL) {
if (dragItem->IsDataType("FUR_SYS")) {
if (sysToMove!=i && sysToMove>=0) {
e->swapSystem(sysToMove,i,preserveChanPos);
}
sysToMove=-1;
}
}
ImGui::EndDragDropTarget();
}
ImGui::TableNextColumn();
if (ImGui::TreeNode(fmt::sprintf("%d. %s##_SYSM%d",i+1,getSystemName(e->song.system[i]),i).c_str())) {
drawSysConf(i,e->song.system[i],e->song.systemFlags[i],true);
ImGui::TreePop();
}
ImGui::TableNextColumn();
ImGui::Button(ICON_FA_CHEVRON_DOWN "##SysChange");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Change");
}
if (ImGui::BeginPopupContextItem("SysPickerC",ImGuiPopupFlags_MouseButtonLeft)) {
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
e->changeSystem(i,picked,preserveChanPos);
updateWindowTitle();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
ImGui::SameLine();
ImGui::BeginDisabled(e->song.systemLen<=1);
if (ImGui::Button(ICON_FA_TIMES "##SysRemove")) {
sysToDelete=i;
showWarning("Are you sure you want to remove this chip?",GUI_WARN_SYSTEM_DEL);
}
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Remove");
}
ImGui::EndDisabled();
ImGui::PopID();
}
if (e->song.systemLen<32) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Button(ICON_FA_PLUS "##SysAdd");
if (ImGui::BeginPopupContextItem("SysPickerA",ImGuiPopupFlags_MouseButtonLeft)) {
DivSystem picked=systemPicker();
if (picked!=DIV_SYSTEM_NULL) {
if (!e->addSystem(picked)) {
showError("cannot add chip! ("+e->getLastError()+")");
}
updateWindowTitle();
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
ImGui::EndTable();
}
}
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) curWindow=GUI_WINDOW_SYS_MANAGER;
ImGui::End();

97
src/gui/sysPicker.cpp Normal file
View file

@ -0,0 +1,97 @@
/**
* Furnace Tracker - multi-system chiptune tracker
* Copyright (C) 2021-2022 tildearrow and contributors
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "gui.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome4.h"
#include "guiConst.h"
#include <imgui.h>
DivSystem FurnaceGUI::systemPicker() {
DivSystem ret=DIV_SYSTEM_NULL;
DivSystem hoveredSys=DIV_SYSTEM_NULL;
bool reissueSearch=false;
if (curSysSection==NULL) {
curSysSection=availableSystems;
}
ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x);
if (ImGui::InputTextWithHint("##SysSearch","Search...",&sysSearchQuery)) reissueSearch=true;
if (ImGui::BeginTabBar("SysCats")) {
for (int i=0; chipCategories[i]; i++) {
if (ImGui::BeginTabItem(chipCategoryNames[i])) {
if (ImGui::IsItemActive()) {
reissueSearch=true;
}
curSysSection=chipCategories[i];
ImGui::EndTabItem();
}
}
ImGui::EndTabBar();
}
if (reissueSearch) {
String lowerCase=sysSearchQuery;
for (char& i: lowerCase) {
if (i>='A' && i<='Z') i+='a'-'A';
}
sysSearchResults.clear();
for (int j=0; curSysSection[j]; j++) {
String lowerCase1=e->getSystemName((DivSystem)curSysSection[j]);
for (char& i: lowerCase1) {
if (i>='A' && i<='Z') i+='a'-'A';
}
if (lowerCase1.find(lowerCase)!=String::npos) {
sysSearchResults.push_back((DivSystem)curSysSection[j]);
}
}
}
if (ImGui::BeginTable("SysList",1,ImGuiTableFlags_ScrollY,ImVec2(500.0f*dpiScale,200.0*dpiScale))) {
if (sysSearchQuery.empty()) {
// display chip list
for (int j=0; curSysSection[j]; j++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(e->getSystemName((DivSystem)curSysSection[j]),false,0,ImVec2(500.0f*dpiScale,0.0f))) ret=(DivSystem)curSysSection[j];
if (ImGui::IsItemHovered()) {
hoveredSys=(DivSystem)curSysSection[j];
}
}
} else {
// display search results
for (DivSystem i: sysSearchResults) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(e->getSystemName(i),false,0,ImVec2(500.0f*dpiScale,0.0f))) ret=i;
if (ImGui::IsItemHovered()) {
hoveredSys=i;
}
}
}
ImGui::EndTable();
}
ImGui::Separator();
if (ImGui::BeginChild("SysDesc",ImVec2(0.0f,150.0f*dpiScale),false,ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse)) {
if (hoveredSys!=DIV_SYSTEM_NULL) {
const DivSysDef* sysDef=e->getSystemDef(hoveredSys);
ImGui::TextWrapped("%s",sysDef->description);
}
}
ImGui::EndChild();
return ret;
}