summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Mittendrein <git@maxmitti.tk>2018-11-03 20:47:45 +0100
committerMarkus Mittendrein <git@maxmitti.tk>2018-11-03 20:47:45 +0100
commit8088d0e0e3bb948d950e55a3452b39226e6750dd (patch)
tree7aafdbe60c45da7b10736f8fe4e7af3c3f73b8e3
parent5426330c7560610843228758684e886d5d672b9c (diff)
downloadDTMenuDebug.c4d-8088d0e0e3bb948d950e55a3452b39226e6750dd.tar.gz
DTMenuDebug.c4d-8088d0e0e3bb948d950e55a3452b39226e6750dd.zip
Add submenu-as-column-feature (includes Breaking change of ActivateEntry)
Some fixes
-rw-r--r--Graphics.pngbin10647 -> 13307 bytes
-rw-r--r--Script.c599
2 files changed, 467 insertions, 132 deletions
diff --git a/Graphics.png b/Graphics.png
index 737dfed..4b53b19 100644
--- a/Graphics.png
+++ b/Graphics.png
Binary files differ
diff --git a/Script.c b/Script.c
index b45ce5f..82824e7 100644
--- a/Script.c
+++ b/Script.c
@@ -88,6 +88,7 @@ static const DT_Menu_Entry_Args = 8;
static const DT_Menu_Entry_Condition = 9;
static const DT_Menu_Entry_Placeholder = 10;
static const DT_Menu_Entry_InstantDescriptionIcon = 11;
+static const DT_Menu_Entry_SubMenuColumnData = 12;
static const Menu_CallbackType_None = 0x0;
static const Menu_CallbackType_Special2 = 0x1;
@@ -124,6 +125,9 @@ static const Menu_CallbackArg_Menu = 9;
static const Menu_CallbackArg_Returned = 10;
static const Menu_CallbackArg_Menu = 11;
static const Menu_CallbackArg_EntryNumber = 12;
+static const Menu_CallbackArg_EntryColumn = 13;
+static const Menu_CallbackArg_NewSelectionColumn = 14;
+static const Menu_CallbackArg_OldSelectionColumn = 15;
static const Menu_CallbackArg_Min = Menu_CallbackArg_All;
static const Menu_CallbackArg_Max = Menu_CallbackArg_EntryNumber;
@@ -145,6 +149,10 @@ static const DT_Menu_Adaptor_EnumSubmenuIcon = 14;
static const DT_Menu_Adaptor_EnumAllowUnknown = 15;
static const DT_Menu_Adaptor_EnumInline = 16;
+static const Menu_Selection_Simple = 0;
+static const Menu_Selection_WithColumn = 1;
+static const Menu_Selection_SubMenuColumnChain = 2;
+
static const Menu_Layout_Icon = 1;
static const Menu_Layout_Description = 2;
@@ -160,12 +168,16 @@ static const Menu_Layout__NoFlagMask = 3;
// ----------------------------------------------------------------------------
local settings;
-local entries;
local createEntries;
local entryCount;
-local currentSelection;
+local currentColumn;
+local columnCount;
+local columnEntries;
+local columnOffsets;
+local currentColumnSelections;
local suspended;
local closing;
+local refreshing;
local msgBoardMode;
local msgBoardEntry;
local noSelectionCallbacks;
@@ -174,6 +186,9 @@ local vars;
func Initialize()
{
+ currentColumnSelections = [-1];
+ columnEntries = [[]];
+ msgBoardEntry = [];
vars = [];
}
@@ -185,11 +200,18 @@ func Destruction()
func Create(array cSettings, array cEntries)
{
settings = cSettings;
- entries = [];
createEntries = cEntries;
- currentSelection = -1;
settings[DT_Menu_Settings_ConditionDisableMode] = settings[DT_Menu_Settings_ConditionDisableMode] || Menu_ConditionReact_Hide;
+ settings[DT_Menu_Settings_InstantDescriptionDecoration] = settings[DT_Menu_Settings_InstantDescriptionDecoration] || DT_Menu_DefaultInstantDescriptionDecoration;
+ if(!settings[DT_Menu_Settings_InstantDescriptionPosition])
+ {
+ settings[DT_Menu_Settings_InstantDescriptionFlags] = settings[DT_Menu_Settings_InstantDescriptionFlags] || MSG_Left | MSG_Bottom;
+ settings[DT_Menu_Settings_InstantDescriptionPosition] = [40, -60];
+ }
+
entryCount = 0;
+ columnCount = 0;
+ columnOffsets = [];
if(settings[DT_Menu_Settings_Vars])
{
@@ -209,7 +231,12 @@ func Create(array cSettings, array cEntries)
factoryArgs[Menu_CallbackArg_Menu] = this;
factoryArgs[Menu_CallbackArg_MenuObject] = settings[DT_Menu_Settings_Object];
- HandleEntries(createEntries, entryCount, entries, factoryArgs);
+ HandleEntries(createEntries, entryCount, columnEntries[0], factoryArgs, 0);
+
+ if(columnCount > 1 && settings[DT_Menu_Settings_Style] != C4MN_Style_Context)
+ {
+ FatalError("CreateNewMenu: Multi column menus (and submenus as columns) are only supported with Menu_Style_Context()");
+ }
if(settings[DT_Menu_Settings_Parent]) settings[DT_Menu_Settings_Parent]->Suspend();
@@ -218,11 +245,30 @@ func Create(array cSettings, array cEntries)
if(GetType(settings[DT_Menu_Settings_Size]) == C4V_Array) SetMenuSize(settings[DT_Menu_Settings_Size][0], settings[DT_Menu_Settings_Size][1], settings[DT_Menu_Settings_Object]);
- AddEntries(entries);
+ // update submenu columns if necessary
+ for(var i = 0; i < GetLength(currentColumnSelections); ++i)
+ {
+ // TODO: cascade? then remove the columnSelection == -1 check
+ var columnSelection = currentColumnSelections[i];
+ if((columnSelection == -1) || !columnEntries[i])
+ {
+ break;
+ }
+
+ if(columnEntries[i][columnSelection][DT_Menu_Entry_SubMenuColumnData])
+ {
+ columnEntries[i + 1] = columnEntries[i][columnSelection][DT_Menu_Entry_SubMenuColumnData][2];
+ }
+ }
+
+ AddEntries();
if(entryCount > 0)
{
- SelectEntry(settings[DT_Menu_Settings_Selection]);
+ if(!refreshing)
+ {
+ SelectEntry(settings[DT_Menu_Settings_Selection]);
+ }
}
else
{
@@ -237,26 +283,43 @@ func Create(array cSettings, array cEntries)
if(!GetEffect("Menu", this)) AddEffect("Menu", this, 1, 1, this, 0);
}
-func SelectEntry(int selection)
+func SelectEntry(indexOrChain, int column)
{
- if(selection < 0) selection = entryCount + selection;
- if(!(selection >= 0 && selection < entryCount)) { FatalError("Assertion failed: SelectEntry: selection index out of range; assertion code: selection >= 0 && selection < entryCount"); }
- SelectMenuItem(selection, settings[DT_Menu_Settings_Object]);
+ if(GetType(indexOrChain) == C4V_Array)
+ {
+ if(!(GetLength(indexOrChain) <= columnCount)) { FatalError("Assertion failed: SelectEntry: index chain is longer than there are columns; assertion code: GetLength(indexOrChain) <= columnCount"); }
+ for(var i = 0; i < GetLength(indexOrChain); ++i)
+ {
+ SelectEntry(indexOrChain[i], i);
+ }
+ }
+ else if(!indexOrChain || GetType(indexOrChain) == C4V_Int)
+ {
+ if(!(column >= 0 && column < GetLength(columnEntries) && GetType(columnEntries[column]) == C4V_Array)) { FatalError("Assertion failed: SelectEntry: column index out of range; assertion code: column >= 0 && column < GetLength(columnEntries) && GetType(columnEntries[column]) == C4V_Array"); }
+ var entryCount = GetLength(columnEntries[column]);
+ if(indexOrChain < 0) indexOrChain = entryCount + indexOrChain;
+ if(!(indexOrChain >= 0 && indexOrChain < entryCount)) { FatalError("Assertion failed: SelectEntry: selection index out of range; assertion code: indexOrChain >= 0 && indexOrChain < entryCount"); }
+ SelectMenuItem(EncodeSelection(indexOrChain, column), settings[DT_Menu_Settings_Object]);
+ }
+ else
+ {
+ FatalError(Format("DT_Menu::SelectEntry: indexOrChain must be an integer or an array of integers but got %d", GetType(indexOrChain)));
+ }
}
-func ActivateEntry(int index, int action)
+func ActivateEntry(int action, indexOrChain, int column)
{
- if(index < 0) index = entryCount + index;
- if(index >= entryCount || index < 0)
+ SelectEntry(indexOrChain);
+
+ var index = indexOrChain;
+ if(GetType(indexOrChain) == C4V_Array)
{
- Log("WARNING: DT_Menu::ActivateEntry: index out of range: %d", index);
- return;
+ column = GetLength(indexOrChain) - 1;
+ index = indexOrChain[column];
}
- SelectEntry(index);
-
- var entry = entries[index];
- MenuItemCommand(entry[DT_Menu_Entry_Icon], index, action);
+ var entry = columnEntries[column][index];
+ MenuItemCommand(entry[DT_Menu_Entry_Icon], EncodeSelection(index, column), action);
}
func SubMenu()
@@ -299,7 +362,7 @@ func FxMenuTimer(object target, int effectNumber, int effectTime)
if(msgBoardMode != 0 && !TestMessageBoard(GetOwner(settings[DT_Menu_Settings_Object]), true))
{
- var entry = entries[msgBoardEntry];
+ var entry = columnEntries[msgBoardEntry[1]][msgBoardEntry[0]];
var args = entry[DT_Menu_Entry_Args];
var reaction = CallCallbacks(args[DT_Menu_Adaptor_Callbacks], Menu_CallbackType_InputAborted, [Menu_CallbackType_InputAborted, entry[DT_Menu_Entry_Icon], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]);
if(reaction != Menu_React_None)
@@ -313,7 +376,7 @@ func FxMenuTimer(object target, int effectNumber, int effectTime)
{
if(settings[DT_Menu_Settings_KeepOpen] & (DT_Menu_KeepOpen_Refresh_Mask | DT_Menu_KeepOpen_Force) && !settings[DT_Menu_Settings_Closable])
{
- Refresh(currentSelection);
+ Refresh(GetSelection(Menu_Selection_WithColumn));
}
else
{
@@ -323,7 +386,7 @@ func FxMenuTimer(object target, int effectNumber, int effectTime)
if(settings[DT_Menu_Settings_KeepOpen] & DT_Menu_KeepOpen_RefreshContinuously && !(effectTime % settings[DT_Menu_Settings_RefreshInterval]))
{
- Refresh(currentSelection);
+ Refresh(GetSelection(Menu_Selection_WithColumn));
}
}
@@ -417,16 +480,29 @@ func Suspend(bool cont)
}
else if(!closing)
{
- Refresh(currentSelection);
+ Refresh(GetSelection(Menu_Selection_WithColumn));
}
}
-func HandleEntries(array factoryEntries, int& i, array& ret, array& factoryArgs)
+func HandleEntries(array factoryEntries, int& i, array& ret, array& factoryArgs, int column)
{
+ if(column >= columnCount)
+ {
+ columnCount = column + 1;
+ }
+
for(var entry in factoryEntries)
{
if(entry[0] == DT_Menu_Type_Entry)
{
+ // is it an extra column submenu?
+ if(entry[2])
+ {
+ var index = 0;
+ HandleEntries(entry[2][1], index, entry[2][2], factoryArgs, column + 1);
+ entry[1][DT_Menu_Entry_SubMenuColumnData] = entry[2];
+ }
+
ret[i++] = entry[1];
}
else if(entry[0] == DT_Menu_Type_Factory)
@@ -436,6 +512,7 @@ func HandleEntries(array factoryEntries, int& i, array& ret, array& factoryArgs)
for(var callback in entry[1][0])
{
factoryArgs[Menu_CallbackArg_EntryNumber] = i;
+ factoryArgs[Menu_CallbackArg_EntryColumn] = column;
var factoryResult = CallA(callback, BindCallbackArgs(factoryArgs, entry[1][2]));
if(GetType(factoryResult) == C4V_Array)
{
@@ -460,97 +537,145 @@ func HandleEntries(array factoryEntries, int& i, array& ret, array& factoryArgs)
}
}
-func AddEntries(array& entries)
+func AddEntries()
{
- for(var i = 0; i < GetLength(entries); ++i)
+ // TODO: determine maximum count of all possible submenu columns; or just ignore because it can be changing anyway?
+ var entriesCount = 0;
+ for(var column in columnEntries)
{
- var entry = entries[i];
- var condition = entry[DT_Menu_Entry_Condition], conditionRet;
- var text = entry[DT_Menu_Entry_Text], noCommand = !entry[DT_Menu_Entry_Placeholder] || (entry[DT_Menu_Entry_Placeholder] != true && !entry[DT_Menu_Entry_Callbacks]);
- if(condition)
+ if(column)
{
- if(noCommand || condition[1] == Menu_Condition_DenySelection || (condition[1] == Menu_Condition_Default && !settings[DT_Menu_Settings_ConditionAllowSelection]))
- {
- noCommand = true;
- }
+ entriesCount = Max(entriesCount, GetLength(column));
+ }
+ }
- conditionRet = CallA(condition[0], [entry[DT_Menu_Entry_Icon], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]) || settings[DT_Menu_Settings_ConditionDisableMode];
- if(conditionRet == Menu_ConditionReact_Hide)
- {
- continue;
- }
- else if(conditionRet == Menu_ConditionReact_GrayOut)
- {
- text = Format("<c 808080>%s</c>", text);
- }
- else if(GetType(conditionRet) == C4V_Array && conditionRet[0] == Menu_ConditionReact_CustomFormat)
- {
- text = Format(conditionRet[1], text);
- }
- else
+ if(columnCount > 1)
+ {
+ SetMenuSize(columnCount, entriesCount, settings[DT_Menu_Settings_Object]);
+ }
+
+ for(var i = 1; i < GetLength(columnEntries); ++i)
+ {
+ var column = columnEntries[i];
+ if(column)
+ {
+ var offset = currentColumnSelections[i - 1] + columnOffsets[i - 1];
+ var entryCount = GetLength(column);
+ if(offset + entryCount > entriesCount)
{
- noCommand = false;
+ offset = entriesCount - entryCount;
}
+ columnOffsets[i] = offset;
}
+ }
- var icon = entry[DT_Menu_Entry_Icon], iconIndex = 0, iconID = 0, deleteIcon = 0;
- if(GetType(icon) == C4V_Array)
+ for(var i = 0, entryIndex = 0; i < entriesCount; ++i)
+ {
+ for(var j = 0; j < columnCount; ++j)
{
- if(GetType(icon[0]) == C4V_C4ID)
+ var rowIndex = i - columnOffsets[j];
+ var entries = columnEntries[j];
+ if(entries && rowIndex >= 0 && rowIndex < GetLength(entries))
{
- iconID = icon[0];
- if(icon[2])
+ var entry = entries[rowIndex];
+ var condition = entry[DT_Menu_Entry_Condition], conditionRet;
+ var text = entry[DT_Menu_Entry_Text], noCommand = !entry[DT_Menu_Entry_Placeholder] || (entry[DT_Menu_Entry_Placeholder] != true && !entry[DT_Menu_Entry_Callbacks]);
+ if(condition)
{
- icon = [CreateIconDummy()->SetIcon(iconID, icon[1])->SetColor(icon[2]), true];
+ if(noCommand || condition[1] == Menu_Condition_DenySelection || (condition[1] == Menu_Condition_Default && !settings[DT_Menu_Settings_ConditionAllowSelection]))
+ {
+ noCommand = true;
+ }
+
+ conditionRet = CallA(condition[0], [entry[DT_Menu_Entry_Icon], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]) || settings[DT_Menu_Settings_ConditionDisableMode];
+ if(conditionRet == Menu_ConditionReact_Hide)
+ {
+ continue;
+ }
+ else if(conditionRet == Menu_ConditionReact_GrayOut)
+ {
+ text = Format("<c 808080>%s</c>", text);
+ }
+ else if(GetType(conditionRet) == C4V_Array && conditionRet[0] == Menu_ConditionReact_CustomFormat)
+ {
+ text = Format(conditionRet[1], text);
+ }
+ else
+ {
+ noCommand = false;
+ }
}
- else if(icon[1])
+
+ var icon = entry[DT_Menu_Entry_Icon], iconIndex = 0, iconID = 0, deleteIcon = 0;
+ if(GetType(icon) == C4V_Array)
{
- entry[DT_Menu_Entry_Extra] |= C4MN_Add_ImgIndexed;
- iconIndex = icon[1];
- entry[DT_Menu_Entry_XPar1] = icon[1];
+ if(GetType(icon[0]) == C4V_C4ID)
+ {
+ iconID = icon[0];
+ if(icon[2])
+ {
+ icon = [CreateIconDummy()->SetIcon(iconID, icon[1])->SetColor(icon[2]), true];
+ }
+ else if(icon[1])
+ {
+ entry[DT_Menu_Entry_Extra] |= C4MN_Add_ImgIndexed;
+ iconIndex = icon[1];
+ entry[DT_Menu_Entry_XPar1] = icon[1];
+ }
+ }
+
+ if(GetType(icon[0]) == C4V_C4Object)
+ {
+ entry[DT_Menu_Entry_Extra] |= C4MN_Add_ImgObject;
+ entry[DT_Menu_Entry_XPar1] = icon[0];
+ if(icon[1])
+ {
+ deleteIcon = icon[0];
+ }
+ iconID = GetID(icon[0]);
+ }
}
- }
+ else
+ {
+ iconID = icon;
+ }
+ entry[DT_Menu_Entry_Icon] = iconID;
- if(GetType(icon[0]) == C4V_C4Object)
- {
- entry[DT_Menu_Entry_Extra] |= C4MN_Add_ImgObject;
- entry[DT_Menu_Entry_XPar1] = icon[0];
- if(icon[1])
+ if(!entry[DT_Menu_Entry_InstantDescriptionIcon])
{
- deleteIcon = icon[0];
+ entry[DT_Menu_Entry_InstantDescriptionIcon] = [iconID, iconIndex];
}
- iconID = GetID(icon[0]);
- }
- }
- else
- {
- iconID = icon;
- }
- entry[DT_Menu_Entry_Icon] = iconID;
- if(!entry[DT_Menu_Entry_InstantDescriptionIcon])
- {
- entry[DT_Menu_Entry_InstantDescriptionIcon] = [iconID, iconIndex];
- }
+ if(settings[DT_Menu_Settings_InstantDescription])
+ {
+ entry[DT_Menu_Entry_Extra] |= C4MN_Add_ForceNoDesc;
+ }
- if(settings[DT_Menu_Settings_InstantDescription])
- {
- entry[DT_Menu_Entry_Extra] |= C4MN_Add_ForceNoDesc;
- }
+ columnEntries[j][rowIndex] = entry;
+ var showDesc = !(entry[DT_Menu_Entry_Extra] & C4MN_Add_ForceNoDesc);
- entries[i] = entry;
- var showDesc = !(entry[DT_Menu_Entry_Extra] & C4MN_Add_ForceNoDesc);
+ if(entry[DT_Menu_Entry_SubMenuColumnData] && currentColumnSelections[j] == rowIndex)
+ {
+ text = Format("%s {{MN7I:6}}", text);
+ }
- AddMenuItem(text, !noCommand && "MenuItemCommand", iconID, settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Count], i, showDesc && entry[DT_Menu_Entry_Description], entry[DT_Menu_Entry_Extra], entry[DT_Menu_Entry_XPar1], entry[DT_Menu_Entry_XPar2]);
+ AddMenuItem(text, !noCommand && "MenuItemCommand", iconID, settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Count], entryIndex, showDesc && entry[DT_Menu_Entry_Description], entry[DT_Menu_Entry_Extra], entry[DT_Menu_Entry_XPar1], entry[DT_Menu_Entry_XPar2]);
- if(deleteIcon)
- {
- RemoveObject(deleteIcon);
+ if(deleteIcon)
+ {
+ RemoveObject(deleteIcon);
+ }
+ }
+ else
+ {
+ AddMenuItem("", "DummyItemCommand", 0, settings[DT_Menu_Settings_Object], 0, entryIndex, 0, C4MN_Add_ForceNoDesc);
+ }
+ ++entryIndex;
}
}
}
-func React(reaction, int itemNumber, int refreshDelayed)
+func React(reaction, array entryIndex, int refreshDelayed)
{
if(reaction == Menu_React_Close)
{
@@ -562,24 +687,25 @@ func React(reaction, int itemNumber, int refreshDelayed)
}
else if(reaction == Menu_React_Refresh || (settings[DT_Menu_Settings_KeepOpen] & DT_Menu_KeepOpen_Refresh))
{
- Refresh(itemNumber, refreshDelayed);
+ Refresh(entryIndex, refreshDelayed);
}
else if(GetType(reaction) == C4V_Array)
{
- var selection = currentSelection;
+ var selection = GetSelection(Menu_Selection_WithColumn);
+ var currentSelection = selection;
if(reaction[0] == Menu_React_SelectionOffset)
{
- selection += reaction[1];
- selection %= entryCount;
+ selection[0] += reaction[1];
+ selection[0] %= entryCount;
}
else if(reaction[0] == Menu_React_SelectionChange)
{
- selection = BoundBy(reaction[1], 0, entryCount - 1);
+ selection[0] = BoundBy(reaction[1], 0, GetLength(columnEntries[selection[1]]) - 1);
}
if(selection != currentSelection)
{
- SelectEntry(selection);
+ SelectEntry(selection[0], selection[1]);
}
}
}
@@ -592,7 +718,8 @@ func CheckCondition(array entry)
func MenuItemCommand(id ID, int itemNumber, int action)
{
- var entry = entries[itemNumber];
+ var column = DecodeSelection(itemNumber);
+ var entry = columnEntries[column][itemNumber];
var condition = entry[DT_Menu_Entry_Condition];
action = action || Menu_CallbackType_Normal;
var reaction;
@@ -611,7 +738,15 @@ func MenuItemCommand(id ID, int itemNumber, int action)
reaction = Menu_React_Refresh;
}
}
- React(reaction, itemNumber);
+ React(reaction, [itemNumber, column]);
+}
+
+// for entries that shouldn't be able to be selected
+// just in else if(conditionRet == Menu_ConditionReact_) they get activated through clicking
+func DummyItemCommand(id ID, int itemNumber, int action)
+{
+ var column = DecodeSelection(itemNumber);
+ React(Menu_React_KeepOpen, [itemNumber, column]);
}
func SubmenuItemCallback(int action, object menuObject, args, array allArgs)
@@ -621,7 +756,14 @@ func SubmenuItemCallback(int action, object menuObject, args, array allArgs)
if(((action & Menu_CallbackType_Defaults) && reaction == Menu_React_None) || reaction == Menu_React_ShowSubmenu)
{
- subMenu = CreateNewMenu(args[2], settings, this);
+ if(args[3])
+ {
+ SelectEntry(0, currentColumn + 1);
+ }
+ else
+ {
+ subMenu = CreateNewMenu(args[2], settings, this);
+ }
return Menu_React_None;
}
else
@@ -630,18 +772,32 @@ func SubmenuItemCallback(int action, object menuObject, args, array allArgs)
}
}
+func DecodeSelection(int& selection)
+{
+ var column = selection % columnCount;
+ selection /= columnCount;
+ selection -= columnOffsets[column];
+ return column;
+}
+
+func EncodeSelection(int selection, int column)
+{
+ return (selection + columnOffsets[column]) * columnCount + column;
+}
+
func MenuQueryCancel(int selection, object menuObject)
{
+ var column = DecodeSelection(selection);
var reaction;
if(selection != -1)
{
- var entry = entries[selection];
+ var entry = columnEntries[column][selection];
if(CheckCondition(entry))
{
reaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], Menu_CallbackType_Close, [Menu_CallbackType_Close, entry[DT_Menu_Entry_Icon], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]);
}
- React(reaction, selection, true);
+ React(reaction, [selection, column], true);
}
if((settings[DT_Menu_Settings_KeepOpen] != DT_Menu_KeepOpen_Not && settings[DT_Menu_Settings_KeepOpen] != DT_Menu_KeepOpen_Permanent && !settings[DT_Menu_Settings_Closable]) || (reaction == Menu_React_KeepOpen))
@@ -652,43 +808,186 @@ func MenuQueryCancel(int selection, object menuObject)
func OnMenuSelection(int selection, object menuObject)
{
- if(!(selection < GetLength(entries))) { FatalError("Assertion failed: OnMenuSelection: Menu selection out of range. Was the menu modified manually?; assertion code: selection < GetLength(entries)"); }
- if(selection != currentSelection)
+ var column = DecodeSelection(selection);
+ var oldColumnSelection = currentColumnSelections[column];
+ var oldColumnOldSelection = currentColumnSelections[currentColumn];
+
+ // selections can be out of range if dummy entries get selected
+ // let's emulate some engine behavior then
+ if(!columnEntries[column])
{
- var oldSelection = currentSelection;
- var entry = entries[currentSelection];
- var deselectReaction = Menu_React_None;
- var selectReaction = Menu_React_None;
- if(!noSelectionCallbacks && CheckCondition(entry) && currentSelection != -1)
+ // navigate downwards when pressing right at the right border (thus selecting a dummy)
+ var selectedColumn = column;
+ for(; column > -1; --column)
{
- deselectReaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], Menu_CallbackType_Deselection, [Menu_CallbackType_Deselection, entry[DT_Menu_Entry_Icon], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args], selection, oldSelection]);
+ if(columnEntries[column])
+ {
+ break;
+ }
}
- entry = entries[currentSelection = selection];
+ if(column >= 0)
+ {
+ var offset = 1;
+ if(selectedColumn == columnCount - 1 && currentColumn == 0)
+ {
+ // but upwards if it was actually pressing left at the left border
+ offset = 0;
+ column = 0;
+ }
+ SelectEntry(BoundBy(selection - columnOffsets[column] + offset, 0, GetLength(columnEntries[column]) - 1), column);
+ }
+ return;
+ }
- if(!noSelectionCallbacks && CheckCondition(entry))
+ var skipHandling;
+ if(selection < 0)
+ {
+ if(currentColumn == column)
{
- selectReaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], Menu_CallbackType_Selection, [Menu_CallbackType_Selection, entry[DT_Menu_Entry_Icon], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args], selection, oldSelection]);
+ if(oldColumnSelection == 0)
+ {
+ // wrap around if the last selection was the top already
+ SelectEntry(GetLength(columnEntries[column]) - 1, column);
+ }
+ else
+ {
+ // otherwise maybe wrapped around from the bottom or just hovered it with the mouse
+ SelectEntry(0, column);
+ }
+ return;
+ }
+ skipHandling = true;
+ }
+
+ if(selection >= GetLength(columnEntries[column]))
+ {
+ if(currentColumn == column)
+ {
+ if(oldColumnSelection == GetLength(columnEntries[column]) - 1)
+ {
+ // wrap around if the last selection was the bottom already
+ SelectEntry(0, column);
+ }
+ else
+ {
+ // otherwise maybe wrapped around from the top or just hovered it with the mouse
+ SelectEntry(GetLength(columnEntries[column]) - 1, column);
+ }
+ return;
+ }
+ skipHandling = true;
+ }
+
+ if(selection != oldColumnSelection || column != currentColumn)
+ {
+ var oldColumn = currentColumn;
+ currentColumn = column;
+ if(!skipHandling)
+ {
+ var entry = columnEntries[oldColumn][oldColumnOldSelection];
+ var deselectReaction = Menu_React_None;
+ var selectReaction = Menu_React_None;
+ if(!noSelectionCallbacks && CheckCondition(entry) && oldColumnSelection != -1)
+ {
+ var args = [Menu_CallbackType_Deselection, entry[DT_Menu_Entry_Icon], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args], selection, oldColumnSelection];
+ args[Menu_CallbackArg_NewSelectionColumn] = currentColumn;
+ args[Menu_CallbackArg_OldSelectionColumn] = oldColumn;
+ deselectReaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], Menu_CallbackType_Deselection, args);
+ }
+
+ var oldIsColumnSubmenu = !!entry[DT_Menu_Entry_SubMenuColumnData];
+
+ currentColumnSelections[column] = selection;
+ entry = columnEntries[column][selection];
+
+ if(!noSelectionCallbacks && CheckCondition(entry))
+ {
+ var args = [Menu_CallbackType_Selection, entry[DT_Menu_Entry_Icon], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args], selection, oldColumnSelection];
+ args[Menu_CallbackArg_NewSelectionColumn] = currentColumn;
+ args[Menu_CallbackArg_OldSelectionColumn] = oldColumn;
+ selectReaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], Menu_CallbackType_Selection, args);
+ }
+
+ if(deselectReaction != Menu_React_None)
+ {
+ React(deselectReaction, GetSelection(Menu_Selection_WithColumn));
+ }
+
+ if(selectReaction != Menu_React_None)
+ {
+ React(selectReaction, GetSelection(Menu_Selection_WithColumn));
+ }
+
+ selection = currentColumnSelections[column];
}
- if(deselectReaction != Menu_React_None)
+ if(selection + columnOffsets[currentColumn] == oldColumnOldSelection + columnOffsets[oldColumn])
{
- React(deselectReaction, currentSelection);
+ if(currentColumn == oldColumn + 1 && oldIsColumnSubmenu)
+ {
+ // column submenu was entered through pressing right; select the first entry
+ // but only if it's not there yet
+ if(selection != 0)
+ {
+ SelectEntry(0, currentColumn);
+ return;
+ }
+ }
+ else if(currentColumn == oldColumn - 1)
+ {
+ // column submenu was left through pressing left; select the submenu entry
+ // but only if it's not there yet
+ if(selection != oldColumnSelection)
+ {
+ SelectEntry(oldColumnSelection, currentColumn);
+ return;
+ }
+ }
}
- if(selectReaction != Menu_React_None)
+ if(skipHandling)
{
- React(selectReaction, currentSelection);
+ currentColumn = oldColumn;
+ return;
}
if(settings[DT_Menu_Settings_InstantDescription])
{
- ShowInstantDescription(entry);
+ ShowInstantDescription(columnEntries[currentColumn][selection]);
+ }
+
+ // update submenu column if necessary
+ if(selection != oldColumnSelection)
+ {
+ if(entry[DT_Menu_Entry_SubMenuColumnData])
+ {
+ SetLength(columnEntries, column + 2);
+
+ for(var i = column + 1; i < columnCount; ++i)
+ {
+ currentColumnSelections[i] = -1;
+ }
+
+ Refresh([selection, column]);
+ return;
+ }
+ else
+ {
+ var oldEntries = columnEntries[column + 1];
+ SetLength(columnEntries, column + 1);
+ currentColumnSelections[column + 1] = -1;
+ if(oldEntries)
+ {
+ Refresh([selection, column]);
+ return;
+ }
+ }
}
}
}
-func Refresh(int selection, bool delayed)
+func Refresh(array selection, bool delayed)
{
if(suspended)
{
@@ -708,8 +1007,15 @@ func Refresh(int selection, bool delayed)
}
CloseMenu(settings[DT_Menu_Settings_Object]);
+
+ var oldRefreshing = refreshing;
+ refreshing = true;
Create(settings, createEntries);
- SelectEntry(BoundBy(selection, 0, entryCount - 1));
+ refreshing = oldRefreshing;
+ var column = selection[1];
+ selection = selection[0];
+ // TODO: BoundBy for column?
+ SelectEntry(BoundBy(selection, 0, GetLength(columnEntries[column]) - 1), column);
if(disabledCallbacks)
{
@@ -914,13 +1220,22 @@ global func Menu_Entry(array settings)
return [DT_Menu_Type_Entry, namedArgs];
}
-global func Menu_SubMenu(array entrySettings, array menuEntry_Settings)
+global func Menu_SubMenu(array entrySettings, array menuEntry_Settings, bool asColumn)
{
if(!(entrySettings)) { FatalError("Assertion failed: Menu_SubMenu: Non-selectable placeholder as submenu entry doesn't make sense; assertion code: entrySettings"); }
if(!(menuEntry_Settings && GetLength(menuEntry_Settings) > 0)) { FatalError("Assertion failed: Menu_SubMenu: Empty submenu doesn't make sense; assertion code: menuEntry_Settings && GetLength(menuEntry_Settings) > 0"); }
var ret = Menu_Entry(entrySettings);
- ret[1][DT_Menu_Entry_Args] = [ret[1][DT_Menu_Entry_Callbacks], ret[1][DT_Menu_Entry_Args], menuEntry_Settings];
+ ret[1][DT_Menu_Entry_Args] = [ret[1][DT_Menu_Entry_Callbacks], ret[1][DT_Menu_Entry_Args], menuEntry_Settings, asColumn];
ret[1][DT_Menu_Entry_Callbacks] = [Menu_Callback(MN7I->MenuObjectCallback("SubmenuItemCallback"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])];
+
+ if(asColumn)
+ {
+ // [menu settings (currently ignored), menu entries, readily populated entries (later in HandleEntries)]
+ var menuData = [[], [], []];
+ MN7I->UncombineAndDistinguish(menuEntry_Settings, menuData[0], menuData[1]);
+ ret[2] = menuData;
+ }
+
return ret;
}
@@ -1114,7 +1429,7 @@ global func Menu_Adaptor(array entrySettings, array adaptorSettings)
}
}
- return Menu_Factory([MN7I->MenuObjectCallback("AdaptorFactory")], [Menu_Entry(entrySettings)[1], adaptorArgs, entrySettings]);
+ return Menu_Factory([MN7I->MenuObjectCallback("AdaptorFactory")], [Menu_Entry(entrySettings)[1], adaptorArgs, entrySettings], [Menu_CallbackArg_Args, Menu_CallbackArg_EntryNumber, Menu_CallbackArg_EntryColumn]);
}
func EnumValPos(array enumVals, array layout, val, bool allowUnknown)
@@ -1215,7 +1530,7 @@ func AdaptorGetLimits(array limits)
return ret;
}
-func AdaptorFactory(args, int entryIndex)
+func AdaptorFactory(args, int entryIndex, int entryColumn)
{
var origArgs = args;
var entry = args[0];
@@ -1230,7 +1545,7 @@ func AdaptorFactory(args, int entryIndex)
var retSubmenu;
args[DT_Menu_Adaptor_Args] = entry[DT_Menu_Entry_Args];
- args[DT_Menu_Adaptor_EntryIndex] = entryIndex;
+ args[DT_Menu_Adaptor_EntryIndex] = [entryIndex, entryColumn];
args[DT_Menu_Adaptor_Callbacks] = args[DT_Menu_Adaptor_Callbacks] || [];
if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Boolean)
{
@@ -1708,7 +2023,7 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
func InputCallback(string input, int plr)
{
- var entry = entries[msgBoardEntry];
+ var entry = columnEntries[msgBoardEntry[1]][msgBoardEntry[0]];
var args = entry[DT_Menu_Entry_Args];
var callbackArgs = args[DT_Menu_Adaptor_Args];
var oldVal = ScopedVar(args[DT_Menu_Adaptor_Variable]);
@@ -1780,9 +2095,6 @@ global func CreateNewMenu(array menu, inheritSettings, object parentMenu)
settings[DT_Menu_Settings_Style] &= ~C4MN_Style_EqualItemHeight; // EqualItemHeight can't be inherited
settings[DT_Menu_Settings_Selection] = 0; // Selection can't be inherited
settings[DT_Menu_Settings_Callbacks] = 0; // Global callbacks can't be inherited (maybe filter callbacks)
- settings[DT_Menu_Settings_InstantDescriptionDecoration] = DT_Menu_DefaultInstantDescriptionDecoration;
- settings[DT_Menu_Settings_InstantDescriptionFlags] = MSG_Left | MSG_Bottom;
- settings[DT_Menu_Settings_InstantDescriptionPosition] = [40, -60];
settings[DT_Menu_Settings_Parent] = parentMenu;
@@ -1892,7 +2204,7 @@ func CallCustom(callback, args)
{
if(GetType(callback) == C4V_Array && GetLength(callback) == 2 && callback[0] == CallbackTarget_Scenario - 1)
{
- return CallA(ObjectCallback(callback[1], this), args, ...);
+ return CallA(ObjectCallback(callback[1], true), args, ...);
}
else
{
@@ -1926,12 +2238,35 @@ func CheckCustomScopedVar(array variable)
func Update()
{
- return Refresh(currentSelection);
+ return Refresh(GetSelection(Menu_Selection_WithColumn));
}
-func GetSelection()
+func GetSelection(int mode)
{
- return currentSelection;
+ if(mode == Menu_Selection_Simple)
+ {
+ return currentColumnSelections[currentColumn];
+ }
+ else if(mode == Menu_Selection_WithColumn)
+ {
+ return [currentColumnSelections[currentColumn], currentColumn];
+ }
+ else if(mode == Menu_Selection_SubMenuColumnChain)
+ {
+ var ret = [];
+
+ for(var i = 0; i < columnEntries; ++i)
+ {
+ if(!columnEntries[i])
+ {
+ break;
+ }
+
+ ArrayAppend(ret, currentColumnSelections[i]);
+ }
+
+ return ret;
+ }
}
func GetObject()