GUI: prepare for status view
This commit is contained in:
parent
fbc34fae02
commit
79f92fab68
|
@ -105,7 +105,7 @@ struct DivChannelState {
|
|||
int vibratoDepth, vibratoRate, vibratoPos, vibratoPosGiant, vibratoDir, vibratoFine;
|
||||
int tremoloDepth, tremoloRate, tremoloPos;
|
||||
unsigned char arp, arpStage, arpTicks, panL, panR, panRL, panRR, lastVibrato, lastPorta;
|
||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff;
|
||||
bool doNote, legato, portaStop, keyOn, keyOff, nowYouCanStop, stopOnOff, releasing;
|
||||
bool arpYield, delayLocked, inPorta, scheduledSlideReset, shorthandPorta, wasShorthandPorta, noteOnInhibit, resetArp;
|
||||
bool wentThroughNote, goneThroughNote;
|
||||
|
||||
|
@ -154,6 +154,7 @@ struct DivChannelState {
|
|||
keyOff(false),
|
||||
nowYouCanStop(true),
|
||||
stopOnOff(false),
|
||||
releasing(false),
|
||||
arpYield(false),
|
||||
delayLocked(false),
|
||||
inPorta(false),
|
||||
|
|
|
@ -591,8 +591,10 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
chan[i].scheduledSlideReset=true;
|
||||
}
|
||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_OFF_ENV,i));
|
||||
chan[i].releasing=true;
|
||||
} else if (pat->data[whatRow][0]==102) { // env release
|
||||
dispatchCmd(DivCommand(DIV_CMD_ENV_RELEASE,i));
|
||||
chan[i].releasing=true;
|
||||
} else if (!(pat->data[whatRow][0]==0 && pat->data[whatRow][1]==0)) {
|
||||
chan[i].oldNote=chan[i].note;
|
||||
chan[i].note=pat->data[whatRow][0]+((signed char)pat->data[whatRow][1])*12;
|
||||
|
@ -1067,6 +1069,7 @@ void DivEngine::processRow(int i, bool afterDelay) {
|
|||
}
|
||||
} else if (!chan[i].noteOnInhibit) {
|
||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,i,chan[i].note,chan[i].volume>>8));
|
||||
chan[i].releasing=false;
|
||||
chan[i].goneThroughNote=true;
|
||||
chan[i].wentThroughNote=true;
|
||||
keyHit[i]=true;
|
||||
|
@ -1366,6 +1369,7 @@ bool DivEngine::nextTick(bool noAccum, bool inhibitLowLat) {
|
|||
//dispatchCmd(DivCommand(DIV_CMD_VOLUME,note.channel,(note.volume*(chan[note.channel].volMax>>8))/127));
|
||||
dispatchCmd(DivCommand(DIV_CMD_NOTE_ON,note.channel,note.note));
|
||||
keyHit[note.channel]=true;
|
||||
chan[note.channel].releasing=false;
|
||||
chan[note.channel].noteOnInhibit=true;
|
||||
chan[note.channel].lastIns=note.ins;
|
||||
} else {
|
||||
|
|
|
@ -6342,7 +6342,18 @@ bool FurnaceGUI::init() {
|
|||
waveSigned=e->getConfBool("waveSigned",false);
|
||||
waveGenVisible=e->getConfBool("waveGenVisible",false);
|
||||
waveEditStyle=e->getConfInt("waveEditStyle",0);
|
||||
extraChannelButtons=e->getConfInt("extraChannelButtons",0);
|
||||
int extraChannelButtons=e->getConfInt("extraChannelButtons",0);
|
||||
if (!e->hasConf("patExtraButtons")) {
|
||||
patExtraButtons=(extraChannelButtons==1);
|
||||
} else {
|
||||
patExtraButtons=e->getConfBool("patExtraButtons",false);
|
||||
}
|
||||
if (!e->hasConf("patChannelNames")) {
|
||||
patChannelNames=(extraChannelButtons==2);
|
||||
} else {
|
||||
patChannelNames=e->getConfBool("patChannelNames",false);
|
||||
}
|
||||
patChannelHints=e->getConfInt("patChannelHints",0);
|
||||
lockLayout=e->getConfBool("lockLayout",false);
|
||||
#ifdef IS_MOBILE
|
||||
fullScreen=true;
|
||||
|
@ -6880,7 +6891,9 @@ void FurnaceGUI::commitState() {
|
|||
e->setConf("waveSigned",waveSigned);
|
||||
e->setConf("waveGenVisible",waveGenVisible);
|
||||
e->setConf("waveEditStyle",waveEditStyle);
|
||||
e->setConf("extraChannelButtons",extraChannelButtons);
|
||||
e->setConf("patExtraButtons",patExtraButtons);
|
||||
e->setConf("patChannelNames",patChannelNames);
|
||||
e->setConf("patChannelHints",(int)patChannelHints);
|
||||
e->setConf("lockLayout",lockLayout);
|
||||
e->setConf("fullScreen",fullScreen);
|
||||
e->setConf("mobileUI",mobileUI);
|
||||
|
@ -7116,7 +7129,6 @@ FurnaceGUI::FurnaceGUI():
|
|||
loopRow(-1),
|
||||
loopEnd(-1),
|
||||
isClipping(0),
|
||||
extraChannelButtons(0),
|
||||
newSongCategory(0),
|
||||
latchTarget(0),
|
||||
wheelX(0),
|
||||
|
@ -7131,6 +7143,10 @@ FurnaceGUI::FurnaceGUI():
|
|||
exitDisabledTimer(0),
|
||||
soloTimeout(0.0f),
|
||||
exportFadeOut(5.0),
|
||||
patExtraButtons(false),
|
||||
patChannelNames(false),
|
||||
patChannelPairs(false),
|
||||
patChannelHints(0),
|
||||
newSongFirstFrame(false),
|
||||
oldRowChanged(false),
|
||||
editControlsOpen(true),
|
||||
|
|
|
@ -305,6 +305,11 @@ enum FurnaceGUIColors {
|
|||
GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,
|
||||
GUI_COLOR_PATTERN_EFFECT_MISC,
|
||||
|
||||
GUI_COLOR_PATTERN_STATUS_OFF,
|
||||
GUI_COLOR_PATTERN_STATUS_REL,
|
||||
GUI_COLOR_PATTERN_STATUS_REL_ON,
|
||||
GUI_COLOR_PATTERN_STATUS_ON,
|
||||
|
||||
GUI_COLOR_SAMPLE_BG,
|
||||
GUI_COLOR_SAMPLE_FG,
|
||||
GUI_COLOR_SAMPLE_LOOP,
|
||||
|
@ -1811,14 +1816,17 @@ class FurnaceGUI {
|
|||
|
||||
DivInstrument* prevInsData;
|
||||
|
||||
int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, exportLoops, soloChan,orderEditMode, orderCursor;
|
||||
int loopOrder, loopRow, loopEnd, isClipping, extraChannelButtons, newSongCategory, latchTarget;
|
||||
int curIns, curWave, curSample, curOctave, curOrder, playOrder, prevIns, oldRow, editStep, exportLoops, soloChan, orderEditMode, orderCursor;
|
||||
int loopOrder, loopRow, loopEnd, isClipping, newSongCategory, latchTarget;
|
||||
int wheelX, wheelY, dragSourceX, dragSourceXFine, dragSourceY, dragDestinationX, dragDestinationXFine, dragDestinationY, oldBeat, oldBar;
|
||||
int curGroove, exitDisabledTimer;
|
||||
float soloTimeout;
|
||||
|
||||
double exportFadeOut;
|
||||
|
||||
bool patExtraButtons, patChannelNames, patChannelPairs;
|
||||
unsigned char patChannelHints;
|
||||
|
||||
bool newSongFirstFrame, oldRowChanged;
|
||||
bool editControlsOpen, ordersOpen, insListOpen, songInfoOpen, patternOpen, insEditOpen;
|
||||
bool waveListOpen, waveEditOpen, sampleListOpen, sampleEditOpen, aboutOpen, settingsOpen;
|
||||
|
|
|
@ -988,6 +988,11 @@ const FurnaceGUIColorDef guiColors[GUI_COLOR_MAX]={
|
|||
D(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"",ImVec4(0.0f,1.0f,0.5f,1.0f)),
|
||||
D(GUI_COLOR_PATTERN_EFFECT_MISC,"",ImVec4(0.3f,0.3f,1.0f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_PATTERN_STATUS_OFF,"",ImVec4(0.2f,0.2f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_PATTERN_STATUS_REL,"",ImVec4(0.7f,0.1f,0.1f,1.0f)),
|
||||
D(GUI_COLOR_PATTERN_STATUS_REL_ON,"",ImVec4(1.0f,0.8f,0.1f,1.0f)),
|
||||
D(GUI_COLOR_PATTERN_STATUS_ON,"",ImVec4(0.3f,1.0f,0.1f,1.0f)),
|
||||
|
||||
D(GUI_COLOR_SAMPLE_BG,"",ImVec4(0.04f,0.13f,0.2f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_FG,"",ImVec4(0.7f,0.7f,0.7f,1.0f)),
|
||||
D(GUI_COLOR_SAMPLE_LOOP,"",ImVec4(0.1f,0.22f,0.35f,1.0f)),
|
||||
|
|
|
@ -408,6 +408,7 @@ void FurnaceGUI::drawPattern() {
|
|||
sel1.xFine^=sel2.xFine;
|
||||
sel2.xFine^=sel1.xFine;
|
||||
}
|
||||
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));
|
||||
|
@ -465,17 +466,11 @@ void FurnaceGUI::drawPattern() {
|
|||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
float lpwStart=ImGui::GetCursorPosX();
|
||||
if (ImGui::Selectable((extraChannelButtons==2)?" --##ExtraChannelButtons":" ++##ExtraChannelButtons",false,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
if (++extraChannelButtons>2) extraChannelButtons=0;
|
||||
if (ImGui::Selectable(" ++##ExtraChannelButtons",false,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
ImGui::OpenPopup("PatternOpt");
|
||||
}
|
||||
if (ImGui::IsItemHovered() && !mobileUI) {
|
||||
if (extraChannelButtons==2) {
|
||||
ImGui::SetTooltip("Pattern names (click to collapse)\nRight-click for visualizer");
|
||||
} else if (extraChannelButtons==1) {
|
||||
ImGui::SetTooltip("Expanded (click for pattern names)\nRight-click for visualizer");
|
||||
} else {
|
||||
ImGui::SetTooltip("Compact (click to expand)\nRight-click for visualizer");
|
||||
}
|
||||
ImGui::SetTooltip("click for pattern options (effect columns/pattern names/visualizer)");
|
||||
}
|
||||
if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) {
|
||||
fancyPattern=!fancyPattern;
|
||||
|
@ -484,6 +479,41 @@ void FurnaceGUI::drawPattern() {
|
|||
e->getCommandStream(cmdStream);
|
||||
cmdStream.clear();
|
||||
}
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,origWinPadding);
|
||||
ImGui::PushFont(mainFont);
|
||||
if (ImGui::BeginPopup("PatternOpt",ImGuiWindowFlags_NoMove|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings)) {
|
||||
ImGui::Text("Options:");
|
||||
ImGui::Indent();
|
||||
ImGui::Checkbox("Effect columns/collapse",&patExtraButtons);
|
||||
ImGui::Checkbox("Pattern names",&patChannelNames);
|
||||
ImGui::Checkbox("Channel group hints",&patChannelPairs);
|
||||
if (ImGui::Checkbox("Visualizer",&fancyPattern)) {
|
||||
inhibitMenu=true;
|
||||
e->enableCommandStream(fancyPattern);
|
||||
e->getCommandStream(cmdStream);
|
||||
cmdStream.clear();
|
||||
}
|
||||
ImGui::Unindent();
|
||||
|
||||
ImGui::Text("Channel status:");
|
||||
ImGui::Indent();
|
||||
if (ImGui::RadioButton("No##_PCS0",patChannelHints==0)) {
|
||||
patChannelHints=0;
|
||||
}
|
||||
if (ImGui::RadioButton("Basic##_PCS0",patChannelHints==1)) {
|
||||
patChannelHints=1;
|
||||
}
|
||||
if (ImGui::RadioButton("Regular##_PCS0",patChannelHints==2)) {
|
||||
patChannelHints=2;
|
||||
}
|
||||
if (ImGui::RadioButton("Detailed##_PCS0",patChannelHints==3)) {
|
||||
patChannelHints=3;
|
||||
}
|
||||
ImGui::Unindent();
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleVar();
|
||||
for (int i=0; i<chans; i++) {
|
||||
if (!e->curSubSong->chanShow[i]) continue;
|
||||
ImGui::TableNextColumn();
|
||||
|
@ -619,7 +649,7 @@ void FurnaceGUI::drawPattern() {
|
|||
minLabelArea.x+=0.5f*(maxLabelArea.x-minLabelArea.x-ImGui::CalcTextSize(chanID).x);
|
||||
}
|
||||
|
||||
if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0.0f,0.0f));
|
||||
if ((!patExtraButtons && !patChannelNames && !patChannelHints) || settings.channelVolStyle!=0) ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing,ImVec2(0.0f,0.0f));
|
||||
|
||||
ImGui::PushID(2048+i);
|
||||
switch (settings.channelStyle) {
|
||||
|
@ -735,7 +765,7 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
ImGui::PopID();
|
||||
|
||||
if (extraChannelButtons==0 || settings.channelVolStyle!=0) ImGui::PopStyleVar();
|
||||
if ((!patExtraButtons && !patChannelNames && !patChannelHints) || settings.channelVolStyle!=0) ImGui::PopStyleVar();
|
||||
|
||||
if (displayTooltip && ImGui::IsItemHovered() && !mobileUI) {
|
||||
ImGui::SetTooltip("%s",e->getChannelName(i));
|
||||
|
@ -834,15 +864,7 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
|
||||
// extra buttons
|
||||
if (extraChannelButtons==2) {
|
||||
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
|
||||
ImGui::PushFont(mainFont);
|
||||
snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
|
||||
if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
editStr(&pat->name);
|
||||
}
|
||||
ImGui::PopFont();
|
||||
} else if (extraChannelButtons==1) {
|
||||
if (patExtraButtons) {
|
||||
snprintf(chanID,2048,"%c##_HCH%d",e->curSubSong->chanCollapse[i]?'+':'-',i);
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX()+4.0f*dpiScale);
|
||||
if (ImGui::SmallButton(chanID)) {
|
||||
|
@ -872,6 +894,110 @@ void FurnaceGUI::drawPattern() {
|
|||
}
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
if (patChannelNames) {
|
||||
DivPattern* pat=e->curPat[i].getPattern(e->curOrders->ord[i][ord],true);
|
||||
ImGui::PushFont(mainFont);
|
||||
snprintf(chanID,2048," %s##PatName%d",pat->name.c_str(),i);
|
||||
if (ImGui::Selectable(chanID,true,ImGuiSelectableFlags_NoPadWithHalfSpacing,ImVec2(0.0f,lineHeight+1.0f*dpiScale))) {
|
||||
editStr(&pat->name);
|
||||
}
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
if (patChannelHints) {
|
||||
DivChannelState* cs=e->getChanState(i);
|
||||
if (cs!=NULL) {
|
||||
ImGui::PushFont(mainFont);
|
||||
if (cs->keyOn) {
|
||||
if (cs->releasing) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_STATUS_REL_ON]));
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_STATUS_ON]));
|
||||
}
|
||||
ImGui::Text(ICON_FA_SQUARE);
|
||||
ImGui::PopStyleColor();
|
||||
} else {
|
||||
if (cs->releasing) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_STATUS_REL]));
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_STATUS_OFF]));
|
||||
}
|
||||
ImGui::Text(ICON_FA_SQUARE);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
ImGui::PopFont();
|
||||
|
||||
if (e->curSubSong->chanCollapse[i]==0) {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_VOLUME_MAX]));
|
||||
ImGui::Text("%.2X",cs->volume>>8);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (cs->volSpeed!=0) {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_VOLUME]));
|
||||
ImGui::Text("%+2d",cs->volSpeed/4);
|
||||
ImGui::PopStyleColor();
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_STATUS_OFF]));
|
||||
ImGui::Text(" 00");
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
if (cs->vibratoDepth>0) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]));
|
||||
ImGui::Text("~%.1X%.1X",cs->vibratoRate,cs->vibratoDepth);
|
||||
ImGui::PopStyleColor();
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_STATUS_OFF]));
|
||||
ImGui::Text("~00");
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
/*
|
||||
if (cs->legato) {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]));
|
||||
ImGui::Text("=");
|
||||
ImGui::PopStyleColor();
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_STATUS_OFF]));
|
||||
ImGui::Text("=");
|
||||
ImGui::PopStyleColor();
|
||||
}*/
|
||||
|
||||
if (cs->inPorta) {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]));
|
||||
if (cs->portaNote<-60 || cs->portaNote>=120) {
|
||||
ImGui::Text("???@%.2X",cs->portaSpeed);
|
||||
} else {
|
||||
ImGui::Text("%s@%.2X",noteNames[60+cs->portaNote],cs->portaSpeed);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
} else {
|
||||
if (cs->portaSpeed>0) {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_EFFECT_PITCH]));
|
||||
if (cs->portaNote<60) {
|
||||
ImGui::Text(" v%.2X ",cs->portaSpeed);
|
||||
} else {
|
||||
ImGui::Text(" ^%.2X ",cs->portaSpeed);
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
} else {
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,ImGui::GetColorU32(uiColors[GUI_COLOR_PATTERN_STATUS_OFF]));
|
||||
ImGui::Text(" --- ");
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::TableNextColumn();
|
||||
lastPatternWidth=ImGui::GetCursorPosX()-lpwStart+ImGui::GetStyle().ScrollbarSize;
|
||||
|
|
|
@ -3020,6 +3020,10 @@ void FurnaceGUI::drawSettings() {
|
|||
UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_SYS_SECONDARY,"Secondary specific effect");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_PATTERN_EFFECT_MISC,"Miscellaneous");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_EE_VALUE,"External command output");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_PATTERN_STATUS_OFF,"Status: off");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_PATTERN_STATUS_REL,"Status: off + macro rel");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_PATTERN_STATUS_REL_ON,"Status: on + macro rel");
|
||||
UI_COLOR_CONFIG(GUI_COLOR_PATTERN_STATUS_ON,"Status: on");
|
||||
ImGui::TreePop();
|
||||
}
|
||||
if (ImGui::TreeNode("Sample Editor")) {
|
||||
|
|
Loading…
Reference in a new issue