From e88e0a4e4ef6ece61866480f7c9463f4905d700c Mon Sep 17 00:00:00 2001 From: aurora Date: Mon, 22 Aug 2022 03:47:00 +0300 Subject: [PATCH 1/3] GUI: Remember window x/y position and maximized state. Warning: This may cause issues when windows are re-ordered. Is there a way to fix windows spawning outside of screen boundaries? --- src/gui/gui.cpp | 61 +++++++++++++++++++++++++++++++++++++------------ src/gui/gui.h | 8 ++++--- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 9189618df..20c3fc58d 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -862,7 +862,7 @@ void FurnaceGUI::stopPreviewNote(SDL_Scancode scancode, bool autoNote) { void FurnaceGUI::noteInput(int num, int key, int vol) { DivPattern* pat=e->curPat[cursor.xCoarse].getPattern(e->curOrders->ord[cursor.xCoarse][curOrder],true); - + prepareUndo(GUI_UNDO_PATTERN_EDIT); if (key==100) { // note off @@ -2103,7 +2103,7 @@ void FurnaceGUI::editOptions(bool topMenu) { snprintf(id,63,"%.2x##LatchFX",data); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[fxColors[data]]); } - + if (ImGui::Selectable(id,latchTarget==3,ImGuiSelectableFlags_DontClosePopups)) { latchTarget=3; latchNibble=false; @@ -2176,7 +2176,7 @@ void FurnaceGUI::editOptions(bool topMenu) { doTranspose(transposeAmount,opMaskTransposeValue); ImGui::CloseCurrentPopup(); } - + ImGui::Separator(); if (ImGui::MenuItem("interpolate",BIND_FOR(GUI_ACTION_PAT_INTERPOLATE))) doInterpolate(); if (ImGui::BeginMenu("change instrument...")) { @@ -2303,7 +2303,7 @@ void FurnaceGUI::toggleMobileUI(bool enable, bool force) { if (mobileUI!=enable || force) { if (!mobileUI && enable) { ImGui::SaveIniSettingsToDisk(finalLayoutPath); - } + } mobileUI=enable; if (mobileUI) { ImGui::GetIO().IniFilename=NULL; @@ -2311,7 +2311,7 @@ void FurnaceGUI::toggleMobileUI(bool enable, bool force) { ImGui::GetIO().IniFilename=finalLayoutPath; ImGui::LoadIniSettingsFromDisk(finalLayoutPath); } - } + } } int _processEvent(void* instance, SDL_Event* event) { @@ -2536,6 +2536,7 @@ bool FurnaceGUI::loop() { if (settings.powerSave) SDL_WaitEventTimeout(NULL,500); } eventTimeBegin=SDL_GetPerformanceCounter(); + bool updateWindow = false; while (SDL_PollEvent(&ev)) { WAKE_UP; ImGui_ImplSDL2_ProcessEvent(&ev); @@ -2642,6 +2643,20 @@ bool FurnaceGUI::loop() { scrW=ev.window.data1/dpiScale; scrH=ev.window.data2/dpiScale; #endif + updateWindow=true; + break; + case SDL_WINDOWEVENT_MOVED: + scrX=ev.window.data1; + scrY=ev.window.data2; + updateWindow=true; + break; + case SDL_WINDOWEVENT_MAXIMIZED: + scrMax=true; + updateWindow=true; + break; + case SDL_WINDOWEVENT_RESTORED: + scrMax=false; + updateWindow=true; break; } break; @@ -2697,6 +2712,18 @@ bool FurnaceGUI::loop() { } } + // update config x/y/w/h values based on scrMax state + if(updateWindow) { + if(scrMax) { + scrConfY=scrConfX = SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(sdlWin)); + } else { + scrConfX=scrX; + scrConfY=scrY; + scrConfW=scrW; + scrConfH=scrH; + } + } + wantCaptureKeyboard=ImGui::GetIO().WantTextInput; if (wantCaptureKeyboard!=oldWantCaptureKeyboard) { @@ -2714,7 +2741,7 @@ bool FurnaceGUI::loop() { if (ImGui::GetIO().MouseDown[0] || ImGui::GetIO().MouseDown[1] || ImGui::GetIO().MouseDown[2] || ImGui::GetIO().MouseDown[3] || ImGui::GetIO().MouseDown[4]) { WAKE_UP; } - + while (true) { midiLock.lock(); if (midiQueue.empty()) { @@ -2870,7 +2897,7 @@ bool FurnaceGUI::loop() { eventTimeEnd=SDL_GetPerformanceCounter(); layoutTimeBegin=SDL_GetPerformanceCounter(); - + ImGui_ImplSDLRenderer_NewFrame(); ImGui_ImplSDL2_NewFrame(sdlWin); ImGui::NewFrame(); @@ -3125,7 +3152,7 @@ bool FurnaceGUI::loop() { if (ImGui::MenuItem("log viewer",BIND_FOR(GUI_ACTION_WINDOW_LOG),logOpen)) logOpen=!logOpen; if (ImGui::MenuItem("statistics",BIND_FOR(GUI_ACTION_WINDOW_STATS),statsOpen)) statsOpen=!statsOpen; if (spoilerOpen) if (ImGui::MenuItem("spoiler",NULL,spoilerOpen)) spoilerOpen=!spoilerOpen; - + ImGui::EndMenu(); } if (ImGui::BeginMenu("help")) { @@ -4263,7 +4290,7 @@ bool FurnaceGUI::loop() { } logD("saving backup..."); SafeWriter* w=e->saveFur(true); - + if (w!=NULL) { FILE* outFile=ps_fopen(backupPath.c_str(),"wb"); if (outFile!=NULL) { @@ -4436,8 +4463,11 @@ bool FurnaceGUI::init() { SDL_Surface* icon=SDL_CreateRGBSurfaceFrom(furIcon,256,256,32,256*4,0xff,0xff00,0xff0000,0xff000000); #endif - scrW=e->getConfInt("lastWindowWidth",1280); - scrH=e->getConfInt("lastWindowHeight",800); + scrW=scrConfW=e->getConfInt("lastWindowWidth",1280); + scrH=scrConfH=e->getConfInt("lastWindowHeight",800); + scrX=scrConfX=e->getConfInt("lastWindowX",SDL_WINDOWPOS_CENTERED); + scrY=scrConfY=e->getConfInt("lastWindowY",SDL_WINDOWPOS_CENTERED); + scrMax=e->getConfBool("lastWindowMax",false); #ifndef __APPLE__ SDL_Rect displaySize; @@ -4453,7 +4483,7 @@ bool FurnaceGUI::init() { SDL_Init(SDL_INIT_VIDEO); - sdlWin=SDL_CreateWindow("Furnace",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)); + sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)); if (sdlWin==NULL) { logE("could not open window! %s",SDL_GetError()); return false; @@ -4619,8 +4649,11 @@ bool FurnaceGUI::finish() { e->setConf("spoilerOpen",spoilerOpen); // commit last window size - e->setConf("lastWindowWidth",scrW); - e->setConf("lastWindowHeight",scrH); + e->setConf("lastWindowWidth",scrConfW); + e->setConf("lastWindowHeight",scrConfH); + e->setConf("lastWindowX",scrConfX); + e->setConf("lastWindowY",scrConfY); + e->setConf("lastWindowMax",scrMax); e->setConf("tempoView",tempoView); e->setConf("waveHex",waveHex); diff --git a/src/gui/gui.h b/src/gui/gui.h index 8a3548f2f..7d42eb84e 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -996,7 +996,9 @@ class FurnaceGUI { FurnaceGUIFileDialog* fileDialog; - int scrW, scrH; + int scrW, scrH, scrConfW, scrConfH; + int scrX, scrY, scrConfX, scrConfY; + bool scrMax; double dpiScale; @@ -1429,7 +1431,7 @@ class FurnaceGUI { int chanToMove; ImVec2 patWindowPos, patWindowSize; - + // pattern view specific ImVec2 fourChars, threeChars, twoChars; ImVec2 noteCellSize, insCellSize, volCellSize, effectCellSize, effectValCellSize; @@ -1505,7 +1507,7 @@ class FurnaceGUI { // visualizer float keyHit[DIV_MAX_CHANS]; int lastIns[DIV_MAX_CHANS]; - + // log window bool followLog; From 8b3c4a84a81a3c9d72c3c1d3624e006be6136e09 Mon Sep 17 00:00:00 2001 From: aurora Date: Mon, 22 Aug 2022 22:05:16 +0300 Subject: [PATCH 2/3] implement bounds check for window spawning --- src/gui/gui.cpp | 40 +++++++++++++++++++++++++++++++++++++--- src/gui/gui.h | 1 + 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 20c3fc58d..204be1af6 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -2517,6 +2517,35 @@ void FurnaceGUI::processPoint(SDL_Event& ev) { } } +// how many pixels should be visible at least at x/y dir +#define OOB_PIXELS_SAFETY 25 + +bool FurnaceGUI::detectOutOfBoundsWindow() { + int count = SDL_GetNumVideoDisplays(); + if(count < 1) { + logW("bounds check: error %s", SDL_GetError()); + return false; + } + + SDL_Rect rect; + for(int i = 0;i < count;i ++) { + if(SDL_GetDisplayUsableBounds(i, &rect) != 0) { + logW("bounds check: error %s", SDL_GetError()); + return false; + } + + bool xbound = (rect.x + OOB_PIXELS_SAFETY) <= (scrX + scrW) && (rect.x + rect.w - OOB_PIXELS_SAFETY) >= scrX; + bool ybound = (rect.y + OOB_PIXELS_SAFETY) <= (scrY + scrH) && (rect.y + rect.h - OOB_PIXELS_SAFETY) >= scrY; + logD("bounds check: display %d is at %dx%dx%dx%d: %s%s", i, rect.x + OOB_PIXELS_SAFETY, rect.y + OOB_PIXELS_SAFETY, rect.x + rect.w - OOB_PIXELS_SAFETY, rect.y + rect.h - OOB_PIXELS_SAFETY, xbound ? "x" : "", ybound ? "y" : ""); + + if(xbound && ybound) { + return true; + } + } + + return false; +} + bool FurnaceGUI::loop() { bool doThreadedInput=!settings.noThreadedInput; if (doThreadedInput) { @@ -2714,9 +2743,7 @@ bool FurnaceGUI::loop() { // update config x/y/w/h values based on scrMax state if(updateWindow) { - if(scrMax) { - scrConfY=scrConfX = SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(sdlWin)); - } else { + if(!scrMax) { scrConfX=scrX; scrConfY=scrY; scrConfW=scrW; @@ -4483,6 +4510,13 @@ bool FurnaceGUI::init() { SDL_Init(SDL_INIT_VIDEO); + // if window would spawn out of bounds, force it to be get default position + if(!detectOutOfBoundsWindow()) { + scrMax = false; + scrX=scrConfX=SDL_WINDOWPOS_CENTERED; + scrY=scrConfY=SDL_WINDOWPOS_CENTERED; + } + sdlWin=SDL_CreateWindow("Furnace",scrX,scrY,scrW*dpiScale,scrH*dpiScale,SDL_WINDOW_RESIZABLE|SDL_WINDOW_ALLOW_HIGHDPI|(scrMax?SDL_WINDOW_MAXIMIZED:0)|(fullScreen?SDL_WINDOW_FULLSCREEN_DESKTOP:0)); if (sdlWin==NULL) { logE("could not open window! %s",SDL_GetError()); diff --git a/src/gui/gui.h b/src/gui/gui.h index 7d42eb84e..f4276f052 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1710,6 +1710,7 @@ class FurnaceGUI { void runBackupThread(); void pushPartBlend(); void popPartBlend(); + bool detectOutOfBoundsWindow(); int processEvent(SDL_Event* ev); bool loop(); bool finish(); From 0e847dc1aab974d8adf18ead6e73eda287f2919b Mon Sep 17 00:00:00 2001 From: aurora Date: Mon, 22 Aug 2022 22:17:19 +0300 Subject: [PATCH 3/3] add setting for choosing whether to save window position --- src/gui/gui.cpp | 4 ++-- src/gui/gui.h | 1 + src/gui/settings.cpp | 35 +++++++++++++++++++++++------------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 204be1af6..76e2b8a16 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4685,8 +4685,8 @@ bool FurnaceGUI::finish() { // commit last window size e->setConf("lastWindowWidth",scrConfW); e->setConf("lastWindowHeight",scrConfH); - e->setConf("lastWindowX",scrConfX); - e->setConf("lastWindowY",scrConfY); + e->setConf("lastWindowX",settings.saveWindowPos?scrConfX:(int)SDL_WINDOWPOS_CENTERED); + e->setConf("lastWindowY",settings.saveWindowPos?scrConfY:(int)SDL_WINDOWPOS_CENTERED); e->setConf("lastWindowMax",scrMax); e->setConf("tempoView",tempoView); diff --git a/src/gui/gui.h b/src/gui/gui.h index f4276f052..21e4ecf53 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1129,6 +1129,7 @@ class FurnaceGUI { int dragMovesSelection; int unsignedDetune; int noThreadedInput; + int saveWindowPos; int clampSamples; int saveUnusedPatterns; int channelColors; diff --git a/src/gui/settings.cpp b/src/gui/settings.cpp index 03c32e655..cb720da43 100644 --- a/src/gui/settings.cpp +++ b/src/gui/settings.cpp @@ -259,7 +259,7 @@ void FurnaceGUI::drawSettings() { } ImGui::Separator(); - + ImGui::Text("Initial system:"); ImGui::SameLine(); if (ImGui::Button("Current system")) { @@ -369,7 +369,7 @@ void FurnaceGUI::drawSettings() { } rightClickable ImGui::SetNextItemWidth(ImGui::GetContentRegionAvail().x-(50.0f*dpiScale)); CWSliderScalar("Panning",ImGuiDataType_S8,&settings.initialSys[i+2],&_MINUS_ONE_HUNDRED_TWENTY_SEVEN,&_ONE_HUNDRED_TWENTY_SEVEN); rightClickable - + // oh please MSVC don't cry if (ImGui::TreeNode("Configure")) { drawSysConf(-1,(DivSystem)settings.initialSys[i],(unsigned int&)settings.initialSys[i+3],false); @@ -452,7 +452,7 @@ void FurnaceGUI::drawSettings() { if (ImGui::Checkbox("Double click selects entire column",&doubleClickColumnB)) { settings.doubleClickColumn=doubleClickColumnB; } - + bool allowEditDockingB=settings.allowEditDocking; if (ImGui::Checkbox("Allow docking editors",&allowEditDockingB)) { settings.allowEditDocking=allowEditDockingB; @@ -509,6 +509,14 @@ void FurnaceGUI::drawSettings() { ImGui::SetTooltip("threaded input processes key presses for note preview on a separate thread (on supported platforms), which reduces latency.\nhowever, crashes have been reported when threaded input is on. enable this option if that is the case."); } + bool saveWindowPosB=settings.saveWindowPos; + if (ImGui::Checkbox("Remember window location",&saveWindowPosB)) { + settings.saveWindowPos=saveWindowPosB; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("remembers where window was last located on start-up. When disabled, window will start in a defaul location."); + } + bool blankInsB=settings.blankIns; if (ImGui::Checkbox("New instruments are blank",&blankInsB)) { settings.blankIns=blankInsB; @@ -639,7 +647,7 @@ void FurnaceGUI::drawSettings() { BUFFER_SIZE_SELECTABLE(2048); ImGui::EndCombo(); } - + ImGui::Text("Quality"); ImGui::SameLine(); ImGui::Combo("##Quality",&settings.audioQuality,audioQualities,2); @@ -697,7 +705,7 @@ void FurnaceGUI::drawSettings() { } if (hasToReloadMidi) { - midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); + midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); midiMap.compile(); } @@ -1344,7 +1352,7 @@ void FurnaceGUI::drawSettings() { if (ImGui::Checkbox("Unsigned FM detune values",&unsignedDetuneB)) { settings.unsignedDetune=unsignedDetuneB; } - + // sorry. temporarily disabled until ImGui has a way to add separators in tables arbitrarily. /*bool sysSeparatorsB=settings.sysSeparators; if (ImGui::Checkbox("Add separators between systems in Orders",&sysSeparatorsB)) { @@ -1553,12 +1561,12 @@ void FurnaceGUI::drawSettings() { UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_MOD,"Mod. accent (secondary)"); UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_MOD,"Mod. border"); UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_MOD,"Mod. border shadow"); - + UI_COLOR_CONFIG(GUI_COLOR_FM_PRIMARY_CAR,"Car. accent (primary"); UI_COLOR_CONFIG(GUI_COLOR_FM_SECONDARY_CAR,"Car. accent (secondary)"); UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_CAR,"Car. border"); UI_COLOR_CONFIG(GUI_COLOR_FM_BORDER_SHADOW_CAR,"Car. border shadow"); - + ImGui::TreePop(); } if (ImGui::TreeNode("Macro Editor")) { @@ -1917,7 +1925,7 @@ void FurnaceGUI::drawSettings() { UI_KEYBIND_CONFIG(GUI_ACTION_PAT_COLLAPSE_ROWS); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_EXPAND_ROWS); UI_KEYBIND_CONFIG(GUI_ACTION_PAT_LATCH); - + // TODO: collapse/expand pattern and song KEYBIND_CONFIG_END; @@ -2213,6 +2221,7 @@ void FurnaceGUI::syncSettings() { settings.dragMovesSelection=e->getConfInt("dragMovesSelection",2); settings.unsignedDetune=e->getConfInt("unsignedDetune",0); settings.noThreadedInput=e->getConfInt("noThreadedInput",0); + settings.saveWindowPos=e->getConfInt("saveWindowPos",1); settings.initialSysName=e->getConfString("initialSysName",""); settings.clampSamples=e->getConfInt("clampSamples",0); settings.noteOffLabel=e->getConfString("noteOffLabel","OFF"); @@ -2313,6 +2322,7 @@ void FurnaceGUI::syncSettings() { clampSetting(settings.dragMovesSelection,0,2); clampSetting(settings.unsignedDetune,0,1); clampSetting(settings.noThreadedInput,0,1); + clampSetting(settings.saveWindowPos,0,1); clampSetting(settings.clampSamples,0,1); clampSetting(settings.saveUnusedPatterns,0,1); clampSetting(settings.channelColors,0,2); @@ -2344,7 +2354,7 @@ void FurnaceGUI::syncSettings() { parseKeybinds(); - midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); + midiMap.read(e->getConfigPath()+DIR_SEPARATOR_STR+"midiIn_"+stripName(settings.midiInDevice)+".cfg"); midiMap.compile(); e->setMidiDirect(midiMap.directChannel); @@ -2458,6 +2468,7 @@ void FurnaceGUI::commitSettings() { e->setConf("dragMovesSelection",settings.dragMovesSelection); e->setConf("unsignedDetune",settings.unsignedDetune); e->setConf("noThreadedInput",settings.noThreadedInput); + e->setConf("saveWindowPos",settings.saveWindowPos); e->setConf("clampSamples",settings.clampSamples); e->setConf("noteOffLabel",settings.noteOffLabel); e->setConf("noteRelLabel",settings.noteRelLabel); @@ -3130,7 +3141,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { if ((iconFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(iconFont_compressed_data,iconFont_compressed_size,e->getConfInt("iconSize",16)*dpiScale,&fc,fontRangeIcon))==NULL) { logE("could not load icon font!"); } - + if (settings.mainFontSize==settings.patFontSize && settings.patFont<5 && builtinFontM[settings.patFont]==builtinFont[settings.mainFont]) { logD("using main font for pat font."); patFont=mainFont; @@ -3164,7 +3175,7 @@ void FurnaceGUI::applyUISettings(bool updateFonts) { } } } - + if ((bigFont=ImGui::GetIO().Fonts->AddFontFromMemoryCompressedTTF(font_plexSans_compressed_data,font_plexSans_compressed_size,40*dpiScale))==NULL) { logE("could not load big UI font!"); }