From 4db45096214a72d32de7e338352c36fba4778a3e Mon Sep 17 00:00:00 2001 From: tildearrow Date: Sat, 27 Dec 2025 18:16:33 -0500 Subject: [PATCH 01/29] GUI: prepare for new pattern renderer likely two stages, depending on how efficient the first one is stage 1: - no more Selectables - using ImDrawList to draw the pattern - perhaps even bypassing that and directly firing quads at the draw queue stage 2: - using textures and tiles to draw the pattern --- CMakeLists.txt | 1 + src/gui/editControls.cpp | 2 + src/gui/gui.cpp | 3 ++ src/gui/gui.h | 2 + src/gui/newPattern.cpp | 102 +++++++++++++++++++++++++++++++++++++++ src/gui/pattern.cpp | 5 ++ 6 files changed, 115 insertions(+) create mode 100644 src/gui/newPattern.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 57b342527..3cdeefe36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -993,6 +993,7 @@ src/gui/memory.cpp src/gui/mixer.cpp src/gui/midiMap.cpp src/gui/multiInsSetup.cpp +src/gui/newPattern.cpp src/gui/newSong.cpp src/gui/orders.cpp src/gui/osc.cpp diff --git a/src/gui/editControls.cpp b/src/gui/editControls.cpp index 3b16af41b..38bda31c3 100644 --- a/src/gui/editControls.cpp +++ b/src/gui/editControls.cpp @@ -476,6 +476,8 @@ void FurnaceGUI::drawMobileControls() { ImGui::EndTable(); } + ImGui::Checkbox("New Pattern",&newPatternRenderer); + ImGui::Separator(); switch (mobScene) { diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 9cab5c014..98be36f9b 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -4931,6 +4931,8 @@ bool FurnaceGUI::loop() { } ImGui::EndMenu(); } + ImGui::Checkbox("New Pattern",&newPatternRenderer); + ImGui::SameLine(); ImGui::PushStyleColor(ImGuiCol_Text,uiColors[GUI_COLOR_PLAYBACK_STAT]); if (e->isPlaying() && settings.playbackTime) { TimeMicros totalTime=e->getCurTime(); @@ -8765,6 +8767,7 @@ FurnaceGUI::FurnaceGUI(): replacePendingSample(false), displayExportingROM(false), displayExportingCS(false), + newPatternRenderer(false), quitNoSave(false), changeCoarse(false), orderLock(false), diff --git a/src/gui/gui.h b/src/gui/gui.h index 008e1a40d..e96267036 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -1747,6 +1747,7 @@ class FurnaceGUI { bool displayPendingIns, pendingInsSingle, displayPendingRawSample, snesFilterHex, modTableHex, displayEditString; bool displayPendingSamples, replacePendingSample; bool displayExportingROM, displayExportingCS; + bool newPatternRenderer; bool quitNoSave; bool changeCoarse; bool orderLock; @@ -3058,6 +3059,7 @@ class FurnaceGUI { void drawGrooves(); void drawOrders(); void drawPattern(); + void drawPatternNew(); void drawInsList(bool asChild=false); void drawInsEdit(); void drawInsSID3(DivInstrument* ins); diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp new file mode 100644 index 000000000..1e0e25941 --- /dev/null +++ b/src/gui/newPattern.cpp @@ -0,0 +1,102 @@ +/** + * Furnace Tracker - multi-system chiptune tracker + * Copyright (C) 2021-2025 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. + */ + +// for suck's fake Clang extension! +#define _USE_MATH_DEFINES +#include "gui.h" +#include "../ta-log.h" +#include "imgui_internal.h" +#include "IconsFontAwesome4.h" +#include "furIcons.h" +#include "misc/cpp/imgui_stdlib.h" +#include "guiConst.h" +#include "../utfutils.h" +#include + +void FurnaceGUI::drawPatternNew() { + if (nextWindow==GUI_WINDOW_PATTERN) { + patternOpen=true; + ImGui::SetNextWindowFocus(); + nextWindow=GUI_WINDOW_NOTHING; + } + if (!patternOpen) return; + + if (e->isPlaying() && followPattern) { + if (oldRowChanged || !e->isStepping()) { + if (e->isStepping()) pendingStepUpdate=1; + cursor.y=oldRow; + cursor.order=curOrder; + if (selStart.xCoarse==selEnd.xCoarse && selStart.xFine==selEnd.xFine && selStart.y==selEnd.y && selStart.order==selEnd.order && !selecting) { + selStart=cursor; + selEnd=cursor; + } + } + } + sel1=selStart; + sel2=selEnd; + if (sel2.order Date: Sat, 27 Dec 2025 19:47:31 -0500 Subject: [PATCH 02/29] GUI: new pattern renderer, part 1 prototype with no scroll or selection --- src/gui/newPattern.cpp | 154 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 1e0e25941..81b6efb87 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -90,9 +90,161 @@ void FurnaceGUI::drawPatternNew() { patWindowSize=ImGui::GetWindowSize(); } - ImGui::Text("The pattern view goes here."); + ImDrawList* dl=ImGui::GetWindowDrawList(); + char id[64]; + int ord=curOrder; + int chans=e->getTotalChannelCount(); + int displayChans=0; + const DivPattern* patCache[DIV_MAX_CHANS]; + for (int i=0; icurSubSong->chanShow[i]) displayChans++; + } + + for (int i=0; icurSubSong->pat[i].getPattern(e->curOrders->ord[i][ord],true); + } ImGui::PushFont(patFont); + float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale); + + // this could be moved somewhere else for performance... + float oneCharSize=ImGui::CalcTextSize("A").x; + fourChars=ImVec2(oneCharSize*4.0f,lineHeight); + threeChars=ImVec2(oneCharSize*3.0f,lineHeight); + twoChars=ImVec2(oneCharSize*2.0f,lineHeight); + oneChar=ImVec2(oneCharSize,lineHeight); + + noteCellSize=threeChars; + noteCellSize.x+=(float)settings.noteCellSpacing*dpiScale; + insCellSize=twoChars; + insCellSize.x+=(float)settings.insCellSpacing*dpiScale; + volCellSize=twoChars; + volCellSize.x+=(float)settings.volCellSpacing*dpiScale; + effectCellSize=twoChars; + effectCellSize.x+=(float)settings.effectCellSpacing*dpiScale; + effectValCellSize=twoChars; + effectValCellSize.x+=(float)settings.effectValCellSpacing*dpiScale; + + ImVec2 top=ImGui::GetCursorScreenPos(); + ImVec2 pos=top; + + ImU32 activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); + ImU32 inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); + ImU32 rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + + // row number + for (int j=0; jcurSubSong->patLen; j++) { + snprintf(id,63,"%3d",j); + dl->AddText(pos,rowIndexColor,id); + pos.y+=lineHeight; + } + + top.x+=threeChars.x+oneChar.x; + pos=top; + + // channels + for (int i=0; icurSubSong->chanShow[i]) continue; + int chanVolMax=e->getMaxVolumeChan(i); + if (chanVolMax<1) chanVolMax=1; + + float thisChannelSize=noteCellSize.x; + if (e->curSubSong->chanCollapse[i]<3) thisChannelSize+=insCellSize.x; + if (e->curSubSong->chanCollapse[i]<2) thisChannelSize+=volCellSize.x; + if (e->curSubSong->chanCollapse[i]<1) thisChannelSize+=(effectCellSize.x+effectValCellSize.x)*e->curSubSong->pat[i].effectCols; + + // rows + for (int j=0; jcurSubSong->patLen; j++) { + const DivPattern* pat=patCache[i]; + + // note + snprintf(id,63,"%.31s",noteName(pat->newData[j][DIV_PAT_NOTE])); + if (pat->newData[j][DIV_PAT_NOTE]==-1) { + dl->AddText(pos,inactiveColor,id); + } else { + dl->AddText(pos,activeColor,id); + } + + // instrument + if (e->curSubSong->chanCollapse[i]<3) { + pos.x+=threeChars.x; + if (pat->newData[j][DIV_PAT_INS]==-1) { + dl->AddText(pos,inactiveColor,emptyLabel2); + } else { + snprintf(id,63,"%.2X",pat->newData[j][DIV_PAT_INS]); + if (pat->newData[j][DIV_PAT_INS]<0 || pat->newData[j][DIV_PAT_INS]>=e->song.insLen) { + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_ERROR]),id); + } else { + DivInstrumentType t=e->song.ins[pat->newData[j][DIV_PAT_INS]]->type; + if (t!=DIV_INS_AMIGA && t!=e->getPreferInsType(i)) { + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_WARN]),id); + } else { + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS]),id); + } + } + } + } + + // volume + if (e->curSubSong->chanCollapse[i]<2) { + pos.x+=twoChars.x; + if (pat->newData[j][DIV_PAT_VOL]==-1) { + dl->AddText(pos,inactiveColor,emptyLabel2); + } else { + int volColor=(pat->newData[j][DIV_PAT_VOL]*127)/chanVolMax; + if (volColor>127) volColor=127; + if (volColor<0) volColor=0; + snprintf(id,63,"%.2X",pat->newData[j][DIV_PAT_VOL]); + dl->AddText(pos,ImGui::GetColorU32(volColors[volColor]),id); + } + } + + // effects + if (e->curSubSong->chanCollapse[i]<1) { + for (int k=0; kcurPat[i].effectCols; k++) { + int index=DIV_PAT_FX(k); + int indexVal=DIV_PAT_FXVAL(k); + ImU32 effectColor=inactiveColor; + + // effect + pos.x+=twoChars.x; + if (pat->newData[j][index]==-1) { + dl->AddText(pos,inactiveColor,emptyLabel2); + } else { + if (pat->newData[j][index]>0xff) { + snprintf(id,63,"??"); + effectColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else { + const unsigned char data=pat->newData[j][index]; + effectColor=ImGui::GetColorU32(uiColors[fxColors[data]]); + if (pat->newData[j][index]>=0x10 || settings.oneDigitEffects==0) { + snprintf(id,63,"%.2X",data); + } else { + snprintf(id,63," %.1X",data); + } + } + dl->AddText(pos,effectColor,id); + } + + // effect value + pos.x+=twoChars.x; + if (pat->newData[j][indexVal]==-1) { + dl->AddText(pos,effectColor,emptyLabel2); + } else { + snprintf(id,63,"%.2X",pat->newData[j][indexVal]); + dl->AddText(pos,effectColor,id); + } + } + } + + // go to next row + pos.x=top.x; + pos.y+=lineHeight; + } + top.x+=thisChannelSize; + pos=top; + } + ImGui::PopFont(); } ImGui::PopStyleVar(); From 95b536f47e69ac3b3e5371ea5a2363db37d7913a Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Dec 2025 01:45:57 -0500 Subject: [PATCH 03/29] GUI: new pattern renderer, part 2 some scrolling --- src/gui/newPattern.cpp | 237 +++++++++++++++++++++++------------------ 1 file changed, 133 insertions(+), 104 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 81b6efb87..383dc3597 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -84,7 +84,7 @@ void FurnaceGUI::drawPatternNew() { ImGui::SetNextWindowPos(patWindowPos); ImGui::SetNextWindowSize(patWindowSize); } - if (ImGui::Begin("PatternNew",&patternOpen,globalWinFlags|(settings.avoidRaisingPattern?ImGuiWindowFlags_NoBringToFrontOnFocus:0)|((settings.cursorFollowsWheel && !selecting)?ImGuiWindowFlags_NoScrollWithMouse:0),_("Pattern"))) { + if (ImGui::Begin("PatternNew",&patternOpen,globalWinFlags|ImGuiWindowFlags_HorizontalScrollbar|(settings.avoidRaisingPattern?ImGuiWindowFlags_NoBringToFrontOnFocus:0)|((settings.cursorFollowsWheel && !selecting)?ImGuiWindowFlags_NoScrollWithMouse:0),_("Pattern"))) { if (!mobileUI) { patWindowPos=ImGui::GetWindowPos(); patWindowSize=ImGui::GetWindowSize(); @@ -106,6 +106,7 @@ void FurnaceGUI::drawPatternNew() { ImGui::PushFont(patFont); float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale); + dummyRows=(ImGui::GetWindowSize().y/lineHeight)/2; // this could be moved somewhere else for performance... float oneCharSize=ImGui::CalcTextSize("A").x; @@ -128,21 +129,11 @@ void FurnaceGUI::drawPatternNew() { ImVec2 top=ImGui::GetCursorScreenPos(); ImVec2 pos=top; - ImU32 activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); - ImU32 inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); - ImU32 rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + ImVec2 size=ImVec2(0.0f,lineHeight*e->curSubSong->patLen); - // row number - for (int j=0; jcurSubSong->patLen; j++) { - snprintf(id,63,"%3d",j); - dl->AddText(pos,rowIndexColor,id); - pos.y+=lineHeight; - } + size.x+=threeChars.x+oneChar.x; - top.x+=threeChars.x+oneChar.x; - pos=top; - - // channels + // TODO: simplify for (int i=0; icurSubSong->chanShow[i]) continue; int chanVolMax=e->getMaxVolumeChan(i); @@ -153,98 +144,136 @@ void FurnaceGUI::drawPatternNew() { if (e->curSubSong->chanCollapse[i]<2) thisChannelSize+=volCellSize.x; if (e->curSubSong->chanCollapse[i]<1) thisChannelSize+=(effectCellSize.x+effectValCellSize.x)*e->curSubSong->pat[i].effectCols; - // rows - for (int j=0; jcurSubSong->patLen; j++) { - const DivPattern* pat=patCache[i]; - - // note - snprintf(id,63,"%.31s",noteName(pat->newData[j][DIV_PAT_NOTE])); - if (pat->newData[j][DIV_PAT_NOTE]==-1) { - dl->AddText(pos,inactiveColor,id); - } else { - dl->AddText(pos,activeColor,id); - } - - // instrument - if (e->curSubSong->chanCollapse[i]<3) { - pos.x+=threeChars.x; - if (pat->newData[j][DIV_PAT_INS]==-1) { - dl->AddText(pos,inactiveColor,emptyLabel2); - } else { - snprintf(id,63,"%.2X",pat->newData[j][DIV_PAT_INS]); - if (pat->newData[j][DIV_PAT_INS]<0 || pat->newData[j][DIV_PAT_INS]>=e->song.insLen) { - dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_ERROR]),id); - } else { - DivInstrumentType t=e->song.ins[pat->newData[j][DIV_PAT_INS]]->type; - if (t!=DIV_INS_AMIGA && t!=e->getPreferInsType(i)) { - dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_WARN]),id); - } else { - dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS]),id); - } - } - } - } - - // volume - if (e->curSubSong->chanCollapse[i]<2) { - pos.x+=twoChars.x; - if (pat->newData[j][DIV_PAT_VOL]==-1) { - dl->AddText(pos,inactiveColor,emptyLabel2); - } else { - int volColor=(pat->newData[j][DIV_PAT_VOL]*127)/chanVolMax; - if (volColor>127) volColor=127; - if (volColor<0) volColor=0; - snprintf(id,63,"%.2X",pat->newData[j][DIV_PAT_VOL]); - dl->AddText(pos,ImGui::GetColorU32(volColors[volColor]),id); - } - } - - // effects - if (e->curSubSong->chanCollapse[i]<1) { - for (int k=0; kcurPat[i].effectCols; k++) { - int index=DIV_PAT_FX(k); - int indexVal=DIV_PAT_FXVAL(k); - ImU32 effectColor=inactiveColor; - - // effect - pos.x+=twoChars.x; - if (pat->newData[j][index]==-1) { - dl->AddText(pos,inactiveColor,emptyLabel2); - } else { - if (pat->newData[j][index]>0xff) { - snprintf(id,63,"??"); - effectColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); - } else { - const unsigned char data=pat->newData[j][index]; - effectColor=ImGui::GetColorU32(uiColors[fxColors[data]]); - if (pat->newData[j][index]>=0x10 || settings.oneDigitEffects==0) { - snprintf(id,63,"%.2X",data); - } else { - snprintf(id,63," %.1X",data); - } - } - dl->AddText(pos,effectColor,id); - } - - // effect value - pos.x+=twoChars.x; - if (pat->newData[j][indexVal]==-1) { - dl->AddText(pos,effectColor,emptyLabel2); - } else { - snprintf(id,63,"%.2X",pat->newData[j][indexVal]); - dl->AddText(pos,effectColor,id); - } - } - } - - // go to next row - pos.x=top.x; - pos.y+=lineHeight; - } - top.x+=thisChannelSize; - pos=top; + size.x+=thisChannelSize; } + ImVec2 minArea=top; + ImVec2 maxArea=ImVec2( + minArea.x+size.x, + minArea.y+size.y + ); + ImRect rect=ImRect(minArea,maxArea); + + // create the view + ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rect,ImGui::GetID("PatternView"))) { + ImU32 activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); + ImU32 inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); + ImU32 rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + + // row number + for (int j=0; jcurSubSong->patLen; j++) { + snprintf(id,63,"%3d",j); + dl->AddText(pos,rowIndexColor,id); + pos.y+=lineHeight; + } + + top.x+=threeChars.x+oneChar.x; + pos=top; + + // channels + for (int i=0; icurSubSong->chanShow[i]) continue; + int chanVolMax=e->getMaxVolumeChan(i); + if (chanVolMax<1) chanVolMax=1; + + float thisChannelSize=noteCellSize.x; + if (e->curSubSong->chanCollapse[i]<3) thisChannelSize+=insCellSize.x; + if (e->curSubSong->chanCollapse[i]<2) thisChannelSize+=volCellSize.x; + if (e->curSubSong->chanCollapse[i]<1) thisChannelSize+=(effectCellSize.x+effectValCellSize.x)*e->curSubSong->pat[i].effectCols; + + // rows + for (int j=0; jcurSubSong->patLen; j++) { + const DivPattern* pat=patCache[i]; + + // note + snprintf(id,63,"%.31s",noteName(pat->newData[j][DIV_PAT_NOTE])); + if (pat->newData[j][DIV_PAT_NOTE]==-1) { + dl->AddText(pos,inactiveColor,id); + } else { + dl->AddText(pos,activeColor,id); + } + + // instrument + if (e->curSubSong->chanCollapse[i]<3) { + pos.x+=threeChars.x; + if (pat->newData[j][DIV_PAT_INS]==-1) { + dl->AddText(pos,inactiveColor,emptyLabel2); + } else { + snprintf(id,63,"%.2X",pat->newData[j][DIV_PAT_INS]); + if (pat->newData[j][DIV_PAT_INS]<0 || pat->newData[j][DIV_PAT_INS]>=e->song.insLen) { + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_ERROR]),id); + } else { + DivInstrumentType t=e->song.ins[pat->newData[j][DIV_PAT_INS]]->type; + if (t!=DIV_INS_AMIGA && t!=e->getPreferInsType(i)) { + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_WARN]),id); + } else { + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS]),id); + } + } + } + } + + // volume + if (e->curSubSong->chanCollapse[i]<2) { + pos.x+=twoChars.x; + if (pat->newData[j][DIV_PAT_VOL]==-1) { + dl->AddText(pos,inactiveColor,emptyLabel2); + } else { + int volColor=(pat->newData[j][DIV_PAT_VOL]*127)/chanVolMax; + if (volColor>127) volColor=127; + if (volColor<0) volColor=0; + snprintf(id,63,"%.2X",pat->newData[j][DIV_PAT_VOL]); + dl->AddText(pos,ImGui::GetColorU32(volColors[volColor]),id); + } + } + + // effects + if (e->curSubSong->chanCollapse[i]<1) { + for (int k=0; kcurPat[i].effectCols; k++) { + int index=DIV_PAT_FX(k); + int indexVal=DIV_PAT_FXVAL(k); + ImU32 effectColor=inactiveColor; + + // effect + pos.x+=twoChars.x; + if (pat->newData[j][index]==-1) { + dl->AddText(pos,inactiveColor,emptyLabel2); + } else { + if (pat->newData[j][index]>0xff) { + snprintf(id,63,"??"); + effectColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else { + const unsigned char data=pat->newData[j][index]; + effectColor=ImGui::GetColorU32(uiColors[fxColors[data]]); + if (pat->newData[j][index]>=0x10 || settings.oneDigitEffects==0) { + snprintf(id,63,"%.2X",data); + } else { + snprintf(id,63," %.1X",data); + } + } + dl->AddText(pos,effectColor,id); + } + + // effect value + pos.x+=twoChars.x; + if (pat->newData[j][indexVal]==-1) { + dl->AddText(pos,effectColor,emptyLabel2); + } else { + snprintf(id,63,"%.2X",pat->newData[j][indexVal]); + dl->AddText(pos,effectColor,id); + } + } + } + + // go to next row + pos.x=top.x; + pos.y+=lineHeight; + } + top.x+=thisChannelSize; + pos=top; + } + } ImGui::PopFont(); } ImGui::PopStyleVar(); From 591a205a1845f667e2fff63e7833cca9c00ea4fc Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Dec 2025 15:54:35 -0500 Subject: [PATCH 04/29] GUI: new pattern renderer, part 3 dummy rows --- src/gui/newPattern.cpp | 203 +++++++++++++++++++++++++---------------- 1 file changed, 125 insertions(+), 78 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 383dc3597..5bc5c7091 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -29,6 +29,16 @@ #include "../utfutils.h" #include +#define SETUP_ORDER_ALPHA \ + if (ord==curOrder) { \ + ImGui::GetStyle().Alpha=origAlpha; \ + } else { \ + ImGui::GetStyle().Alpha=disabledAlpha; \ + } \ + activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); \ + inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); \ + rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + void FurnaceGUI::drawPatternNew() { if (nextWindow==GUI_WINDOW_PATTERN) { patternOpen=true; @@ -92,21 +102,23 @@ void FurnaceGUI::drawPatternNew() { ImDrawList* dl=ImGui::GetWindowDrawList(); char id[64]; - int ord=curOrder; + int firstOrd=curOrder; int chans=e->getTotalChannelCount(); + /* int displayChans=0; - const DivPattern* patCache[DIV_MAX_CHANS]; for (int i=0; icurSubSong->chanShow[i]) displayChans++; - } - - for (int i=0; icurSubSong->pat[i].getPattern(e->curOrders->ord[i][ord],true); - } + }*/ ImGui::PushFont(patFont); float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale); dummyRows=(ImGui::GetWindowSize().y/lineHeight)/2; + int totalRows=e->curSubSong->patLen+dummyRows*2; + int firstRow=-dummyRows; + while (firstRow<0) { + firstRow+=e->curSubSong->patLen; + firstOrd--; + } // this could be moved somewhere else for performance... float oneCharSize=ImGui::CalcTextSize("A").x; @@ -129,7 +141,7 @@ void FurnaceGUI::drawPatternNew() { ImVec2 top=ImGui::GetCursorScreenPos(); ImVec2 pos=top; - ImVec2 size=ImVec2(0.0f,lineHeight*e->curSubSong->patLen); + ImVec2 size=ImVec2(0.0f,lineHeight*totalRows); size.x+=threeChars.x+oneChar.x; @@ -160,12 +172,26 @@ void FurnaceGUI::drawPatternNew() { ImU32 activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); ImU32 inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); ImU32 rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + float origAlpha=ImGui::GetStyle().Alpha; + float disabledAlpha=ImGui::GetStyle().Alpha*ImGui::GetStyle().DisabledAlpha; // row number - for (int j=0; jcurSubSong->patLen; j++) { - snprintf(id,63,"%3d",j); - dl->AddText(pos,rowIndexColor,id); - pos.y+=lineHeight; + { + int ord=firstOrd; + int row=firstRow; + SETUP_ORDER_ALPHA; + for (int j=0; j=0 && ordcurSubSong->ordersLen) { + snprintf(id,63,"%3d",row); + dl->AddText(pos,rowIndexColor,id); + } + if (++row>=e->curSubSong->patLen) { + row=0; + ord++; + SETUP_ORDER_ALPHA; + } + pos.y+=lineHeight; + } } top.x+=threeChars.x+oneChar.x; @@ -174,6 +200,8 @@ void FurnaceGUI::drawPatternNew() { // channels for (int i=0; icurSubSong->chanShow[i]) continue; + int ord=firstOrd; + int row=firstRow; int chanVolMax=e->getMaxVolumeChan(i); if (chanVolMax<1) chanVolMax=1; @@ -182,97 +210,116 @@ void FurnaceGUI::drawPatternNew() { if (e->curSubSong->chanCollapse[i]<2) thisChannelSize+=volCellSize.x; if (e->curSubSong->chanCollapse[i]<1) thisChannelSize+=(effectCellSize.x+effectValCellSize.x)*e->curSubSong->pat[i].effectCols; + const DivPattern* pat=NULL; + if (ord>=0 && ordcurSubSong->ordersLen) { + pat=e->curSubSong->pat[i].getPattern(e->curOrders->ord[i][ord],true); + } + + SETUP_ORDER_ALPHA; + // rows - for (int j=0; jcurSubSong->patLen; j++) { - const DivPattern* pat=patCache[i]; - - // note - snprintf(id,63,"%.31s",noteName(pat->newData[j][DIV_PAT_NOTE])); - if (pat->newData[j][DIV_PAT_NOTE]==-1) { - dl->AddText(pos,inactiveColor,id); - } else { - dl->AddText(pos,activeColor,id); - } - - // instrument - if (e->curSubSong->chanCollapse[i]<3) { - pos.x+=threeChars.x; - if (pat->newData[j][DIV_PAT_INS]==-1) { - dl->AddText(pos,inactiveColor,emptyLabel2); + for (int j=0; jnewData[row][DIV_PAT_NOTE])); + if (pat->newData[row][DIV_PAT_NOTE]==-1) { + dl->AddText(pos,inactiveColor,id); } else { - snprintf(id,63,"%.2X",pat->newData[j][DIV_PAT_INS]); - if (pat->newData[j][DIV_PAT_INS]<0 || pat->newData[j][DIV_PAT_INS]>=e->song.insLen) { - dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_ERROR]),id); - } else { - DivInstrumentType t=e->song.ins[pat->newData[j][DIV_PAT_INS]]->type; - if (t!=DIV_INS_AMIGA && t!=e->getPreferInsType(i)) { - dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_WARN]),id); - } else { - dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS]),id); - } - } + dl->AddText(pos,activeColor,id); } - } - // volume - if (e->curSubSong->chanCollapse[i]<2) { - pos.x+=twoChars.x; - if (pat->newData[j][DIV_PAT_VOL]==-1) { - dl->AddText(pos,inactiveColor,emptyLabel2); - } else { - int volColor=(pat->newData[j][DIV_PAT_VOL]*127)/chanVolMax; - if (volColor>127) volColor=127; - if (volColor<0) volColor=0; - snprintf(id,63,"%.2X",pat->newData[j][DIV_PAT_VOL]); - dl->AddText(pos,ImGui::GetColorU32(volColors[volColor]),id); - } - } - - // effects - if (e->curSubSong->chanCollapse[i]<1) { - for (int k=0; kcurPat[i].effectCols; k++) { - int index=DIV_PAT_FX(k); - int indexVal=DIV_PAT_FXVAL(k); - ImU32 effectColor=inactiveColor; - - // effect - pos.x+=twoChars.x; - if (pat->newData[j][index]==-1) { + // instrument + if (e->curSubSong->chanCollapse[i]<3) { + pos.x+=threeChars.x; + if (pat->newData[row][DIV_PAT_INS]==-1) { dl->AddText(pos,inactiveColor,emptyLabel2); } else { - if (pat->newData[j][index]>0xff) { - snprintf(id,63,"??"); - effectColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + snprintf(id,63,"%.2X",pat->newData[row][DIV_PAT_INS]); + if (pat->newData[row][DIV_PAT_INS]<0 || pat->newData[row][DIV_PAT_INS]>=e->song.insLen) { + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_ERROR]),id); } else { - const unsigned char data=pat->newData[j][index]; - effectColor=ImGui::GetColorU32(uiColors[fxColors[data]]); - if (pat->newData[j][index]>=0x10 || settings.oneDigitEffects==0) { - snprintf(id,63,"%.2X",data); + DivInstrumentType t=e->song.ins[pat->newData[row][DIV_PAT_INS]]->type; + if (t!=DIV_INS_AMIGA && t!=e->getPreferInsType(i)) { + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS_WARN]),id); } else { - snprintf(id,63," %.1X",data); + dl->AddText(pos,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INS]),id); } } - dl->AddText(pos,effectColor,id); } + } - // effect value + // volume + if (e->curSubSong->chanCollapse[i]<2) { pos.x+=twoChars.x; - if (pat->newData[j][indexVal]==-1) { - dl->AddText(pos,effectColor,emptyLabel2); + if (pat->newData[row][DIV_PAT_VOL]==-1) { + dl->AddText(pos,inactiveColor,emptyLabel2); } else { - snprintf(id,63,"%.2X",pat->newData[j][indexVal]); - dl->AddText(pos,effectColor,id); + int volColor=(pat->newData[row][DIV_PAT_VOL]*127)/chanVolMax; + if (volColor>127) volColor=127; + if (volColor<0) volColor=0; + snprintf(id,63,"%.2X",pat->newData[row][DIV_PAT_VOL]); + dl->AddText(pos,ImGui::GetColorU32(volColors[volColor]),id); + } + } + + // effects + if (e->curSubSong->chanCollapse[i]<1) { + for (int k=0; kcurPat[i].effectCols; k++) { + int index=DIV_PAT_FX(k); + int indexVal=DIV_PAT_FXVAL(k); + ImU32 effectColor=inactiveColor; + + // effect + pos.x+=twoChars.x; + if (pat->newData[row][index]==-1) { + dl->AddText(pos,inactiveColor,emptyLabel2); + } else { + if (pat->newData[row][index]>0xff) { + snprintf(id,63,"??"); + effectColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_INVALID]); + } else { + const unsigned char data=pat->newData[row][index]; + effectColor=ImGui::GetColorU32(uiColors[fxColors[data]]); + if (pat->newData[row][index]>=0x10 || settings.oneDigitEffects==0) { + snprintf(id,63,"%.2X",data); + } else { + snprintf(id,63," %.1X",data); + } + } + dl->AddText(pos,effectColor,id); + } + + // effect value + pos.x+=twoChars.x; + if (pat->newData[row][indexVal]==-1) { + dl->AddText(pos,effectColor,emptyLabel2); + } else { + snprintf(id,63,"%.2X",pat->newData[row][indexVal]); + dl->AddText(pos,effectColor,id); + } } } } // go to next row + if (++row>=e->curSubSong->patLen) { + row=0; + ord++; + if (ord>=0 && ordcurSubSong->ordersLen) { + pat=e->curSubSong->pat[i].getPattern(e->curOrders->ord[i][ord],true); + } else { + pat=NULL; + } + SETUP_ORDER_ALPHA; + } pos.x=top.x; pos.y+=lineHeight; } top.x+=thisChannelSize; pos=top; } + + ImGui::GetStyle().Alpha=origAlpha; } ImGui::PopFont(); } From aec2389f033815949ba49fa1e1cfc367a677cdab Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Dec 2025 17:26:38 -0500 Subject: [PATCH 05/29] GUI: new pattern renderer, part 4 borders and highlight --- src/gui/newPattern.cpp | 69 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 5bc5c7091..19e515821 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -39,6 +39,9 @@ inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); \ rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); +// this is ImGui's TABLE_BORDER_SIZE. +#define PAT_BORDER_SIZE 1.0f + void FurnaceGUI::drawPatternNew() { if (nextWindow==GUI_WINDOW_PATTERN) { patternOpen=true; @@ -144,10 +147,14 @@ void FurnaceGUI::drawPatternNew() { ImVec2 size=ImVec2(0.0f,lineHeight*totalRows); size.x+=threeChars.x+oneChar.x; + size.x+=PAT_BORDER_SIZE; // TODO: simplify for (int i=0; icurSubSong->chanShow[i]) continue; + patChanX[i]=size.x; + if (!e->curSubSong->chanShow[i]) { + continue; + } int chanVolMax=e->getMaxVolumeChan(i); if (chanVolMax<1) chanVolMax=1; @@ -157,7 +164,11 @@ void FurnaceGUI::drawPatternNew() { if (e->curSubSong->chanCollapse[i]<1) thisChannelSize+=(effectCellSize.x+effectValCellSize.x)*e->curSubSong->pat[i].effectCols; size.x+=thisChannelSize; + size.x+=PAT_BORDER_SIZE; } + patChanX[chans]=size.x; + + size.x+=oneChar.x; ImVec2 minArea=top; ImVec2 maxArea=ImVec2( @@ -175,15 +186,39 @@ void FurnaceGUI::drawPatternNew() { float origAlpha=ImGui::GetStyle().Alpha; float disabledAlpha=ImGui::GetStyle().Alpha*ImGui::GetStyle().DisabledAlpha; - // row number + // row number and highlights { int ord=firstOrd; int row=firstRow; + bool isPlaying=e->isPlaying(); SETUP_ORDER_ALPHA; for (int j=0; j=0 && ordcurSubSong->ordersLen) { snprintf(id,63,"%3d",row); dl->AddText(pos,rowIndexColor,id); + + ImU32 thisRowBg=0; + if (edit && cursor.y==row && cursor.order==ord && curWindowLast==GUI_WINDOW_PATTERN) { + if (editClone && !isPatUnique && secondTimer<0.5) { + thisRowBg=ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING_CLONE]); + } else { + thisRowBg=ImGui::GetColorU32(uiColors[GUI_COLOR_EDITING]); + } + } else if (isPlaying && oldRow==row && ord==playOrder) { + thisRowBg=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_PLAY_HEAD]); + } else if (e->curSubSong->hilightB>0 && !(row%e->curSubSong->hilightB)) { + thisRowBg=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_2]); + } else if (e->curSubSong->hilightA>0 && !(row%e->curSubSong->hilightA)) { + thisRowBg=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_HI_1]); + } + + if (thisRowBg) { + dl->AddRectFilled( + ImVec2(top.x+patChanX[0],pos.y), + ImVec2(top.x+patChanX[chans],pos.y+lineHeight), + thisRowBg + ); + } } if (++row>=e->curSubSong->patLen) { row=0; @@ -194,22 +229,25 @@ void FurnaceGUI::drawPatternNew() { } } - top.x+=threeChars.x+oneChar.x; - pos=top; - // channels for (int i=0; icurSubSong->chanShow[i]) continue; + + ImVec2 thisTop=ImVec2(top.x+patChanX[i],top.y); + pos=thisTop; + + dl->AddLine( + ImVec2(thisTop.x-PAT_BORDER_SIZE*0.5,thisTop.y), + ImVec2(thisTop.x-PAT_BORDER_SIZE*0.5,maxArea.y), + ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_TableBorderLight]), + PAT_BORDER_SIZE + ); + int ord=firstOrd; int row=firstRow; int chanVolMax=e->getMaxVolumeChan(i); if (chanVolMax<1) chanVolMax=1; - float thisChannelSize=noteCellSize.x; - if (e->curSubSong->chanCollapse[i]<3) thisChannelSize+=insCellSize.x; - if (e->curSubSong->chanCollapse[i]<2) thisChannelSize+=volCellSize.x; - if (e->curSubSong->chanCollapse[i]<1) thisChannelSize+=(effectCellSize.x+effectValCellSize.x)*e->curSubSong->pat[i].effectCols; - const DivPattern* pat=NULL; if (ord>=0 && ordcurSubSong->ordersLen) { pat=e->curSubSong->pat[i].getPattern(e->curOrders->ord[i][ord],true); @@ -312,13 +350,18 @@ void FurnaceGUI::drawPatternNew() { } SETUP_ORDER_ALPHA; } - pos.x=top.x; + pos.x=thisTop.x; pos.y+=lineHeight; } - top.x+=thisChannelSize; - pos=top; } + dl->AddLine( + ImVec2(top.x+patChanX[chans]-PAT_BORDER_SIZE*0.5,top.y), + ImVec2(top.x+patChanX[chans]-PAT_BORDER_SIZE*0.5,maxArea.y), + ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_TableBorderLight]), + PAT_BORDER_SIZE + ); + ImGui::GetStyle().Alpha=origAlpha; } ImGui::PopFont(); From 96ff998278d7f60f45adac4629752045455709f6 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Dec 2025 19:02:02 -0500 Subject: [PATCH 06/29] GUI: new pattern renderer, part 5 stuff --- src/gui/newPattern.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 19e515821..7f2d40e06 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -97,6 +97,16 @@ void FurnaceGUI::drawPatternNew() { ImGui::SetNextWindowPos(patWindowPos); ImGui::SetNextWindowSize(patWindowSize); } + + if (e->isPlaying() && followPattern && (!e->isStepping() || pendingStepUpdate)) updateScroll(oldRow); + if (--pendingStepUpdate<0) pendingStepUpdate=0; + if (nextScroll>-0.5f) { + ImGui::SetNextWindowScroll(ImVec2(-1.0f,nextScroll)); + nextScroll=-1.0f; + nextAddScroll=0.0f; + nextAddScrollX=0.0f; + } + if (ImGui::Begin("PatternNew",&patternOpen,globalWinFlags|ImGuiWindowFlags_HorizontalScrollbar|(settings.avoidRaisingPattern?ImGuiWindowFlags_NoBringToFrontOnFocus:0)|((settings.cursorFollowsWheel && !selecting)?ImGuiWindowFlags_NoScrollWithMouse:0),_("Pattern"))) { if (!mobileUI) { patWindowPos=ImGui::GetWindowPos(); @@ -117,7 +127,7 @@ void FurnaceGUI::drawPatternNew() { float lineHeight=(ImGui::GetTextLineHeight()+2*dpiScale); dummyRows=(ImGui::GetWindowSize().y/lineHeight)/2; int totalRows=e->curSubSong->patLen+dummyRows*2; - int firstRow=-dummyRows; + int firstRow=-dummyRows+1; while (firstRow<0) { firstRow+=e->curSubSong->patLen; firstOrd--; @@ -149,7 +159,6 @@ void FurnaceGUI::drawPatternNew() { size.x+=threeChars.x+oneChar.x; size.x+=PAT_BORDER_SIZE; - // TODO: simplify for (int i=0; icurSubSong->chanShow[i]) { @@ -176,6 +185,7 @@ void FurnaceGUI::drawPatternNew() { minArea.y+size.y ); ImRect rect=ImRect(minArea,maxArea); + ImRect winRect=ImRect(ImGui::GetWindowPos(),ImGui::GetWindowPos()+ImGui::GetWindowSize()); // create the view ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); @@ -229,13 +239,20 @@ void FurnaceGUI::drawPatternNew() { } } - // channels + // selection/cursor background + // TODO + + // channels and borders for (int i=0; icurSubSong->chanShow[i]) continue; ImVec2 thisTop=ImVec2(top.x+patChanX[i],top.y); pos=thisTop; + // check bounds + if (thisTop.x>=winRect.Max.x) break; + if (top.x+patChanX[i+1]AddLine( ImVec2(thisTop.x-PAT_BORDER_SIZE*0.5,thisTop.y), ImVec2(thisTop.x-PAT_BORDER_SIZE*0.5,maxArea.y), @@ -257,6 +274,7 @@ void FurnaceGUI::drawPatternNew() { // rows for (int j=0; j=winRect.Max.y) break; if (pat) { // note snprintf(id,63,"%.31s",noteName(pat->newData[row][DIV_PAT_NOTE])); From f9365a3804b9ab40fa99620ccb63b2b7cfbee080 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Tue, 30 Dec 2025 20:09:30 -0500 Subject: [PATCH 07/29] GUI: new pattern renderer, part 6 bullshit that doesn't work --- src/gui/gui.h | 1 + src/gui/newPattern.cpp | 54 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/gui/gui.h b/src/gui/gui.h index e96267036..9b1ec5e37 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -3016,6 +3016,7 @@ class FurnaceGUI { float calcBPM(const DivGroovePattern& speeds, float hz, int vN, int vD); + ImVec2 mapSelPoint(const SelectionPoint& s, float lineHeight); void patternRow(int i, bool isPlaying, float lineHeight, int chans, int ord, const DivPattern** patCache, bool inhibitSel); void drawMacroEdit(FurnaceGUIMacroDesc& i, int totalFit, float availableWidth, int index); diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 7f2d40e06..859cd8db6 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -42,6 +42,12 @@ // this is ImGui's TABLE_BORDER_SIZE. #define PAT_BORDER_SIZE 1.0f +ImVec2 FurnaceGUI::mapSelPoint(const SelectionPoint& s, float lineHeight) { + int mappedXCoarse=s.xCoarse; + int mappedXFine=s.xFine; + return ImVec2(0,0); +} + void FurnaceGUI::drawPatternNew() { if (nextWindow==GUI_WINDOW_PATTERN) { patternOpen=true; @@ -239,8 +245,52 @@ void FurnaceGUI::drawPatternNew() { } } - // selection/cursor background - // TODO + // selection background + { + int ord=firstOrd; + int row=firstRow; + bool isPlaying=e->isPlaying(); + int curSelFindStage=0; + ImRect selRect; + SETUP_ORDER_ALPHA; + // we find the selection's Y position. + for (int j=0; jAddRectFilled( + selRect.Min, + selRect.Max, + ImGui::GetColorU32(); + ) + curSelFindStage=3; + break; + } + + if (++row>=e->curSubSong->patLen) { + row=0; + ord++; + SETUP_ORDER_ALPHA; + } + pos.y+=lineHeight; + } + } // channels and borders for (int i=0; i Date: Tue, 30 Dec 2025 20:11:13 -0500 Subject: [PATCH 08/29] GUI: my battery is low --- src/gui/newPattern.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 859cd8db6..ca3d9fbf0 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -277,7 +277,7 @@ void FurnaceGUI::drawPatternNew() { dl->AddRectFilled( selRect.Min, selRect.Max, - ImGui::GetColorU32(); + ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_SELECTION]) ) curSelFindStage=3; break; From 61e3b1c4f2fb3c78ffb29cbe365b47048b0a2af2 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 31 Dec 2025 06:38:10 -0500 Subject: [PATCH 09/29] GUI: new pattern renderer, part 7 it still doesn't work --- src/gui/newPattern.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index ca3d9fbf0..990ebd8fd 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -43,8 +43,6 @@ #define PAT_BORDER_SIZE 1.0f ImVec2 FurnaceGUI::mapSelPoint(const SelectionPoint& s, float lineHeight) { - int mappedXCoarse=s.xCoarse; - int mappedXFine=s.xFine; return ImVec2(0,0); } @@ -249,7 +247,6 @@ void FurnaceGUI::drawPatternNew() { { int ord=firstOrd; int row=firstRow; - bool isPlaying=e->isPlaying(); int curSelFindStage=0; ImRect selRect; SETUP_ORDER_ALPHA; @@ -278,7 +275,7 @@ void FurnaceGUI::drawPatternNew() { selRect.Min, selRect.Max, ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_SELECTION]) - ) + ); curSelFindStage=3; break; } From a572fee4e6ff0565cb7b12564067387a26af5491 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Wed, 31 Dec 2025 20:09:47 -0500 Subject: [PATCH 10/29] GUI: new pattern renderer, part 8 cursor/pointer early work --- src/gui/newPattern.cpp | 150 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 6 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 990ebd8fd..8d79df60b 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -118,6 +118,7 @@ void FurnaceGUI::drawPatternNew() { } ImDrawList* dl=ImGui::GetWindowDrawList(); + float patFineOffsets[DIV_MAX_COLS]; char id[64]; int firstOrd=curOrder; int chans=e->getTotalChannelCount(); @@ -155,6 +156,21 @@ void FurnaceGUI::drawPatternNew() { effectValCellSize=twoChars; effectValCellSize.x+=(float)settings.effectValCellSpacing*dpiScale; + float cellSizeAccum=0.0f; + patFineOffsets[DIV_PAT_NOTE]=cellSizeAccum; + cellSizeAccum+=noteCellSize.x; + patFineOffsets[DIV_PAT_INS]=cellSizeAccum; + cellSizeAccum+=insCellSize.x; + patFineOffsets[DIV_PAT_VOL]=cellSizeAccum; + cellSizeAccum+=volCellSize.x; + for (int i=0; i=top.x && pointerPos.xcurSubSong->chanShow[i]) continue; + if (pointerPos.x>=patChanX[i] && pointerPos.x=noteCellSize.x && e->curSubSong->chanCollapse[i]<3) { + pointer.xFine=1; + fineOffset-=noteCellSize.x; + + if (fineOffset>=insCellSize.x && e->curSubSong->chanCollapse[i]<2) { + pointer.xFine=2; + fineOffset-=insCellSize.x; + + if (fineOffset>=volCellSize.x && e->curSubSong->chanCollapse[i]<1) { + pointer.xFine=3; + fineOffset-=volCellSize.x; + + for (int k=0; kcurPat[i].effectCols; k++) { + if (fineOffset>=effectCellSize.x) { + pointer.xFine++; + fineOffset-=effectCellSize.x; + if (fineOffset>=effectValCellSize.x) { + pointer.xFine++; + fineOffset-=effectValCellSize.x; + } else { + break; + } + } else { + break; + } + } + + if (pointer.xFine>2+2*e->curPat[i].effectCols) pointer.xFine=2+2*e->curPat[i].effectCols; + } + } + } + + break; + } + } + + { + int ord=firstOrd; + int row=firstRow; + pos=top; + for (int j=0; j=0 && ordcurSubSong->ordersLen) { + if (pointerPos.y>=pos.y && pointerPos.y<(pos.y+lineHeight)) { + pointer.order=ord; + pointer.y=row; + break; + } + } + if (++row>=e->curSubSong->patLen) { + row=0; + ord++; + } + pos.y+=lineHeight; + } + } + + String debugText=fmt::sprintf("CURSOR: %d:%d, %d/%d",pointer.xCoarse,pointer.xFine,pointer.order,pointer.y); + dl->AddText(top+ImGui::GetCurrentWindow()->Scroll,0xffffffff,debugText.c_str()); + // row number and highlights { int ord=firstOrd; int row=firstRow; bool isPlaying=e->isPlaying(); + pos=top; SETUP_ORDER_ALPHA; for (int j=0; j=0 && ordcurSubSong->ordersLen) { @@ -244,11 +338,13 @@ void FurnaceGUI::drawPatternNew() { } // selection background - { + if (sel1.xCoarse>=0 && sel1.xCoarse=0 && sel2.xCoarseAddRectFilled( selRect.Min, selRect.Max, - ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_SELECTION]) + ImGui::ColorConvertFloat4ToU32(uiColors[GUI_COLOR_PATTERN_SELECTION]) ); curSelFindStage=3; break; @@ -289,6 +384,31 @@ void FurnaceGUI::drawPatternNew() { } } + // cursor background + if (cursor.xCoarse>=0 && cursor.xCoarseAddRectFilled( + ImVec2(top.x+patChanX[cursor.xCoarse]+patFineOffsets[cursor.xFine&31],pos.y), + ImVec2(top.x+patChanX[cursor.xCoarse]+patFineOffsets[(1+cursor.xFine)&31],pos.y+lineHeight), + ImGui::ColorConvertFloat4ToU32(uiColors[GUI_COLOR_PATTERN_CURSOR]) + ); + break; + } + + if (++row>=e->curSubSong->patLen) { + row=0; + ord++; + SETUP_ORDER_ALPHA; + } + pos.y+=lineHeight; + } + } + // channels and borders for (int i=0; icurSubSong->chanShow[i]) continue; @@ -428,6 +548,24 @@ void FurnaceGUI::drawPatternNew() { ); ImGui::GetStyle().Alpha=origAlpha; + + // test for selection + //if (pointer. + if (ImGui::IsWindowHovered(/*ImGuiHoveredFlags_AllowWhenBlockedByActiveItem*/)) { + if (ImRect(dl->GetClipRectMin(),dl->GetClipRectMax()).Contains(ImGui::GetMousePos())) { + dl->AddText(top+ImVec2(0,lineHeight),0xffffffff,"Hovered!!!!!!!"); + } + //updateSelection(0,0,i,ord,true); + } + /* + if (ImGui::IsWindowClicked()) { + //startSelection(0,0,i,ord,true); + } + if (ImGui::IsWindowActive() && CHECK_LONG_HOLD) { + ImGui::InhibitInertialScroll(); + NOTIFY_LONG_HOLD; + mobilePatSel=true; + }*/ } ImGui::PopFont(); } From 308be1bf3968bb6065bb0f301ffd0c5f74d2e17d Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Jan 2026 17:56:54 -0500 Subject: [PATCH 11/29] GUI: new pattern renderer, part 9 selection and input (no hover yet) --- src/gui/newPattern.cpp | 81 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 15 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 8d79df60b..09afa1719 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -138,6 +138,7 @@ void FurnaceGUI::drawPatternNew() { firstOrd--; } + // calculate sizes // this could be moved somewhere else for performance... float oneCharSize=ImGui::CalcTextSize("A").x; fourChars=ImVec2(oneCharSize*4.0f,lineHeight); @@ -199,6 +200,39 @@ void FurnaceGUI::drawPatternNew() { size.x+=oneChar.x; + // add scroll if required + if (nextAddScroll!=0.0f) { + float newScroll=ImGui::GetScrollY()+nextAddScroll; + // wrap around and go to previous/next pattern if we're about to go beyond the view + if (newScroll<0.0f && curOrder>0) { + ImGui::SetScrollY(ImGui::GetScrollMaxY()+newScroll); + if (!e->isPlaying() || !followPattern) setOrder(curOrder-1); + } else if (newScroll>ImGui::GetScrollMaxY() && curOrder<(e->curSubSong->ordersLen-1)) { + ImGui::SetScrollY(newScroll-ImGui::GetScrollMaxY()); + if (!e->isPlaying() || !followPattern) setOrder(curOrder+1); + } else { + ImGui::SetScrollY(newScroll); + } + + // select in empty space + if (nextAddScroll>0.0f) { + updateSelection(selEnd.xCoarse,selEnd.xFine,bottomMostRow,bottomMostOrder); + } else { + updateSelection(selEnd.xCoarse,selEnd.xFine,topMostRow,topMostOrder); + } + + nextScroll=-1.0f; + nextAddScroll=0.0f; + } + if (nextAddScrollX!=0.0f) { + ImGui::SetScrollX(ImGui::GetScrollX()+nextAddScrollX); + nextAddScrollX=0.0f; + } + + topMostOrder=-1; + topMostRow=-1; + + // prepare the view ImVec2 minArea=top; ImVec2 maxArea=ImVec2( minArea.x+size.x, @@ -410,6 +444,7 @@ void FurnaceGUI::drawPatternNew() { } // channels and borders + bool isFirstChan=true; for (int i=0; icurSubSong->chanShow[i]) continue; @@ -442,7 +477,19 @@ void FurnaceGUI::drawPatternNew() { // rows for (int j=0; j=winRect.Max.y) break; - if (pat) { + if (pat && pos.y+lineHeight>=winRect.Min.y) { + if (isFirstChan) { + // set the top-most and bottom-most Y positions + if (topMostOrder==-1) { + topMostOrder=ord; + } + if (topMostRow==-1) { + topMostRow=row; + } + bottomMostOrder=ord; + bottomMostRow=row; + } + // note snprintf(id,63,"%.31s",noteName(pat->newData[row][DIV_PAT_NOTE])); if (pat->newData[row][DIV_PAT_NOTE]==-1) { @@ -538,6 +585,8 @@ void FurnaceGUI::drawPatternNew() { pos.x=thisTop.x; pos.y+=lineHeight; } + + isFirstChan=false; } dl->AddLine( @@ -550,22 +599,24 @@ void FurnaceGUI::drawPatternNew() { ImGui::GetStyle().Alpha=origAlpha; // test for selection - //if (pointer. - if (ImGui::IsWindowHovered(/*ImGuiHoveredFlags_AllowWhenBlockedByActiveItem*/)) { - if (ImRect(dl->GetClipRectMin(),dl->GetClipRectMax()).Contains(ImGui::GetMousePos())) { - dl->AddText(top+ImVec2(0,lineHeight),0xffffffff,"Hovered!!!!!!!"); + if (pointer.xCoarse>=0 && pointer.y>=0 && pointer.order>=0) { + if (ImGui::IsWindowHovered()) { + if (ImRect(dl->GetClipRectMin(),dl->GetClipRectMax()).Contains(ImGui::GetMousePos())) { + dl->AddText(top+ImVec2(0,lineHeight)+ImGui::GetCurrentWindow()->Scroll,0xffffffff,"Hovered!!!!!!!"); + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) { + startSelection(pointer.xCoarse,pointer.xFine,pointer.y,pointer.order); + } + + updateSelection(pointer.xCoarse,pointer.xFine,pointer.y,pointer.order); + + if (ImGui::IsMouseDown(ImGuiMouseButton_Left) && CHECK_LONG_HOLD) { + ImGui::InhibitInertialScroll(); + NOTIFY_LONG_HOLD; + mobilePatSel=true; + } + } } - //updateSelection(0,0,i,ord,true); } - /* - if (ImGui::IsWindowClicked()) { - //startSelection(0,0,i,ord,true); - } - if (ImGui::IsWindowActive() && CHECK_LONG_HOLD) { - ImGui::InhibitInertialScroll(); - NOTIFY_LONG_HOLD; - mobilePatSel=true; - }*/ } ImGui::PopFont(); } From b8f378e20a3161da7c9d84f510a983c555d4d7ea Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Jan 2026 21:04:02 -0500 Subject: [PATCH 12/29] GUI: new pattern renderer, part 10 prepare for frozen items once this reaches feature parity I'll merge and begin field trials --- src/gui/newPattern.cpp | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 09afa1719..43c0ea463 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -37,7 +37,7 @@ } \ activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); \ inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); \ - rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + //rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); // this is ImGui's TABLE_BORDER_SIZE. #define PAT_BORDER_SIZE 1.0f @@ -173,13 +173,11 @@ void FurnaceGUI::drawPatternNew() { patFineOffsets[DIV_PAT_FX(DIV_MAX_EFFECTS)]=cellSizeAccum; ImVec2 top=ImGui::GetCursorScreenPos(); + ImVec2 topRows=top+ImVec2(ImGui::GetScrollX(),0); ImVec2 pos=top; ImVec2 size=ImVec2(0.0f,lineHeight*totalRows); - size.x+=threeChars.x+oneChar.x; - size.x+=PAT_BORDER_SIZE; - for (int i=0; icurSubSong->chanShow[i]) { @@ -233,6 +231,16 @@ void FurnaceGUI::drawPatternNew() { topMostRow=-1; // prepare the view + ImVec2 sizeRows=ImVec2(threeChars.x+oneChar.x+PAT_BORDER_SIZE,lineHeight*totalRows); + ImVec2 minAreaRows=topRows; + ImVec2 maxAreaRows=ImVec2( + minAreaRows.x+sizeRows.x, + minAreaRows.y+sizeRows.y + ); + ImRect rectRows=ImRect(minAreaRows,maxAreaRows); + + top.x+=sizeRows.x; + ImVec2 minArea=top; ImVec2 maxArea=ImVec2( minArea.x+size.x, @@ -244,21 +252,22 @@ void FurnaceGUI::drawPatternNew() { // create the view ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("PatternView1"),NULL,ImGuiItemFlags_AllowOverlap)) { - //bool hovered=ImGui::ItemHoverable(rect,ImGui::GetID("PatternView1"),ImGuiItemFlags_AllowOverlap); ImU32 activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); ImU32 inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); - ImU32 rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + //ImU32 rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); float origAlpha=ImGui::GetStyle().Alpha; float disabledAlpha=ImGui::GetStyle().Alpha*ImGui::GetStyle().DisabledAlpha; // calculate X and Y position of mouse cursor SelectionPoint pointer=SelectionPoint(-1,0,-1,-1); - ImVec2 pointerPos=ImGui::GetMousePos()+ImVec2(ImGui::GetScrollX(),0); + ImVec2 pointerPos=ImGui::GetMousePos()-ImVec2(top.x,0); // special value for row index + // TODO: just no. not ever. + /* if (pointerPos.x>=top.x && pointerPos.xcurSubSong->chanShow[i]) continue; @@ -327,7 +336,7 @@ void FurnaceGUI::drawPatternNew() { String debugText=fmt::sprintf("CURSOR: %d:%d, %d/%d",pointer.xCoarse,pointer.xFine,pointer.order,pointer.y); dl->AddText(top+ImGui::GetCurrentWindow()->Scroll,0xffffffff,debugText.c_str()); - // row number and highlights + // row highlights { int ord=firstOrd; int row=firstRow; @@ -336,9 +345,8 @@ void FurnaceGUI::drawPatternNew() { SETUP_ORDER_ALPHA; for (int j=0; j=0 && ordcurSubSong->ordersLen) { - snprintf(id,63,"%3d",row); - dl->AddText(pos,rowIndexColor,id); - + /*snprintf(id,63,"%3d",row); + dl->AddText(pos,rowIndexColor,id);*/ ImU32 thisRowBg=0; if (edit && cursor.y==row && cursor.order==ord && curWindowLast==GUI_WINDOW_PATTERN) { if (editClone && !isPatUnique && secondTimer<0.5) { @@ -618,6 +626,16 @@ void FurnaceGUI::drawPatternNew() { } } } + + // pattern rows (frozen in place) + ImGui::SetCursorScreenPos(topRows); + ImGui::ItemSize(sizeRows,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rectRows,ImGui::GetID("PatternRows"),NULL,ImGuiItemFlags_AllowOverlap)) { + dl->AddText(topRows+ImVec2(0,lineHeight*2.0),0xffffffff,"FUCK"); + } + + // channel headers (frozen in place) + ImGui::PopFont(); } ImGui::PopStyleVar(); From f55e244760ecc932f9149f80db4c07e4e98daba4 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Jan 2026 22:47:00 -0500 Subject: [PATCH 13/29] GUI: new pattern renderer, part 11 clipping --- src/gui/newPattern.cpp | 43 +++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 43c0ea463..bb9b5d851 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -37,7 +37,7 @@ } \ activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); \ inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); \ - //rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); // this is ImGui's TABLE_BORDER_SIZE. #define PAT_BORDER_SIZE 1.0f @@ -249,14 +249,17 @@ void FurnaceGUI::drawPatternNew() { ImRect rect=ImRect(minArea,maxArea); ImRect winRect=ImRect(ImGui::GetWindowPos(),ImGui::GetWindowPos()+ImGui::GetWindowSize()); + ImU32 activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); + ImU32 inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); + ImU32 rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); + float origAlpha=ImGui::GetStyle().Alpha; + float disabledAlpha=ImGui::GetStyle().Alpha*ImGui::GetStyle().DisabledAlpha; + // create the view + dl->PushClipRect(topRows+ImVec2(sizeRows.x,0),winRect.Max,true); + ImGui::SetCursorScreenPos(top); ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("PatternView1"),NULL,ImGuiItemFlags_AllowOverlap)) { - ImU32 activeColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ACTIVE]); - ImU32 inactiveColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_INACTIVE]); - //ImU32 rowIndexColor=ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_ROW_INDEX]); - float origAlpha=ImGui::GetStyle().Alpha; - float disabledAlpha=ImGui::GetStyle().Alpha*ImGui::GetStyle().DisabledAlpha; // calculate X and Y position of mouse cursor SelectionPoint pointer=SelectionPoint(-1,0,-1,-1); @@ -626,12 +629,38 @@ void FurnaceGUI::drawPatternNew() { } } } + dl->PopClipRect(); // pattern rows (frozen in place) ImGui::SetCursorScreenPos(topRows); ImGui::ItemSize(sizeRows,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rectRows,ImGui::GetID("PatternRows"),NULL,ImGuiItemFlags_AllowOverlap)) { - dl->AddText(topRows+ImVec2(0,lineHeight*2.0),0xffffffff,"FUCK"); + // pattern rows + int ord=firstOrd; + int row=firstRow; + pos=topRows; + SETUP_ORDER_ALPHA; + for (int j=0; j=0 && ordcurSubSong->ordersLen) { + snprintf(id,63,"%3d",row); + dl->AddText(pos,rowIndexColor,id); + } + if (++row>=e->curSubSong->patLen) { + row=0; + ord++; + SETUP_ORDER_ALPHA; + } + pos.y+=lineHeight; + } + + ImGui::GetStyle().Alpha=origAlpha; + + dl->AddLine( + ImVec2(maxAreaRows.x-PAT_BORDER_SIZE*0.5,topRows.y), + ImVec2(maxAreaRows.x-PAT_BORDER_SIZE*0.5,maxArea.y), + ImGui::ColorConvertFloat4ToU32(ImGui::GetStyle().Colors[ImGuiCol_TableBorderLight]), + PAT_BORDER_SIZE + ); } // channel headers (frozen in place) From 1ced369a347e95cdc6e1396e2c2cf441c8cd1aa0 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Thu, 1 Jan 2026 22:57:46 -0500 Subject: [PATCH 14/29] GUI: new pattern renderer, part 12 prepare for channel headers --- src/gui/newPattern.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index bb9b5d851..8ae3d649a 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -172,6 +172,8 @@ void FurnaceGUI::drawPatternNew() { } patFineOffsets[DIV_PAT_FX(DIV_MAX_EFFECTS)]=cellSizeAccum; + ImGui::Dummy(ImVec2(1.0f,ImGui::GetFrameHeight())); + ImVec2 top=ImGui::GetCursorScreenPos(); ImVec2 topRows=top+ImVec2(ImGui::GetScrollX(),0); ImVec2 pos=top; @@ -260,7 +262,6 @@ void FurnaceGUI::drawPatternNew() { ImGui::SetCursorScreenPos(top); ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("PatternView1"),NULL,ImGuiItemFlags_AllowOverlap)) { - // calculate X and Y position of mouse cursor SelectionPoint pointer=SelectionPoint(-1,0,-1,-1); ImVec2 pointerPos=ImGui::GetMousePos()-ImVec2(top.x,0); From 9ae092ed7016cba0d40a30dd0975192b3dfcffc8 Mon Sep 17 00:00:00 2001 From: tildearrow Date: Fri, 2 Jan 2026 20:14:49 -0500 Subject: [PATCH 15/29] GUI: new pattern renderer, part 13 paste channel header code guaranteed not to work --- src/gui/newPattern.cpp | 704 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 700 insertions(+), 4 deletions(-) diff --git a/src/gui/newPattern.cpp b/src/gui/newPattern.cpp index 8ae3d649a..7a67980f2 100644 --- a/src/gui/newPattern.cpp +++ b/src/gui/newPattern.cpp @@ -54,6 +54,8 @@ void FurnaceGUI::drawPatternNew() { } if (!patternOpen) return; + bool inhibitMenu=false; + if (e->isPlaying() && followPattern) { if (oldRowChanged || !e->isStepping()) { if (e->isStepping()) pendingStepUpdate=1; @@ -93,7 +95,7 @@ void FurnaceGUI::drawPatternNew() { sel2.xFine^=sel1.xFine; } - //ImVec2 origWinPadding=ImGui::GetStyle().WindowPadding; + ImVec2 origWinPadding=ImGui::GetStyle().WindowPadding; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,ImVec2(0.0f,0.0f)); if (mobileUI) { patWindowPos=(portrait?ImVec2(0.0f,(mobileMenuPos*-0.65*canvasH)+(0.12*canvasW)):ImVec2((0.16*canvasH)+0.5*canvasW*mobileMenuPos,0.0f)); @@ -172,10 +174,9 @@ void FurnaceGUI::drawPatternNew() { } patFineOffsets[DIV_PAT_FX(DIV_MAX_EFFECTS)]=cellSizeAccum; - ImGui::Dummy(ImVec2(1.0f,ImGui::GetFrameHeight())); - ImVec2 top=ImGui::GetCursorScreenPos(); ImVec2 topRows=top+ImVec2(ImGui::GetScrollX(),0); + ImVec2 topHeaders=top+ImVec2(0,ImGui::GetScrollY()); ImVec2 pos=top; ImVec2 size=ImVec2(0.0f,lineHeight*totalRows); @@ -241,7 +242,17 @@ void FurnaceGUI::drawPatternNew() { ); ImRect rectRows=ImRect(minAreaRows,maxAreaRows); + ImVec2 sizeHeaders=ImVec2(size.x+sizeRows.x,ImGui::GetFrameHeight()); + ImVec2 minAreaHeaders=topHeaders; + ImVec2 maxAreaHeaders=ImVec2( + minAreaHeaders.x+sizeHeaders.x, + minAreaHeaders.y+sizeHeaders.y + ); + ImRect rectHeaders=ImRect(minAreaHeaders,maxAreaHeaders); + top.x+=sizeRows.x; + top.y+=sizeHeaders.y; + topRows.y+=sizeHeaders.y; ImVec2 minArea=top; ImVec2 maxArea=ImVec2( @@ -258,7 +269,7 @@ void FurnaceGUI::drawPatternNew() { float disabledAlpha=ImGui::GetStyle().Alpha*ImGui::GetStyle().DisabledAlpha; // create the view - dl->PushClipRect(topRows+ImVec2(sizeRows.x,0),winRect.Max,true); + dl->PushClipRect(ImVec2(topRows.x+sizeRows.x,topHeaders.y+sizeHeaders.y),winRect.Max,true); ImGui::SetCursorScreenPos(top); ImGui::ItemSize(size,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rect,ImGui::GetID("PatternView1"),NULL,ImGuiItemFlags_AllowOverlap)) { @@ -634,6 +645,7 @@ void FurnaceGUI::drawPatternNew() { // pattern rows (frozen in place) ImGui::SetCursorScreenPos(topRows); + dl->PushClipRect(ImVec2(topRows.x+sizeRows.x,topHeaders.y+sizeHeaders.y),winRect.Max,true); ImGui::ItemSize(sizeRows,ImGui::GetStyle().FramePadding.y); if (ImGui::ItemAdd(rectRows,ImGui::GetID("PatternRows"),NULL,ImGuiItemFlags_AllowOverlap)) { // pattern rows @@ -663,12 +675,696 @@ void FurnaceGUI::drawPatternNew() { PAT_BORDER_SIZE ); } + dl->PopClipRect(); // channel headers (frozen in place) + ImGui::SetCursorScreenPos(topHeaders); + ImGui::ItemSize(sizeHeaders,ImGui::GetStyle().FramePadding.y); + if (ImGui::ItemAdd(rectHeaders,ImGui::GetID("PatternHeaders"),NULL,ImGuiItemFlags_AllowOverlap)) { + //// THE PREVIOUS MESS. + char chanID[2048]; + // draw channel headers + for (int i=0; i