diff options
| author | Markus Mittendrein <git@maxmitti.tk> | 2018-11-03 20:47:45 +0100 |
|---|---|---|
| committer | Markus Mittendrein <git@maxmitti.tk> | 2018-11-03 20:47:45 +0100 |
| commit | 8088d0e0e3bb948d950e55a3452b39226e6750dd (patch) | |
| tree | 7aafdbe60c45da7b10736f8fe4e7af3c3f73b8e3 | |
| parent | 5426330c7560610843228758684e886d5d672b9c (diff) | |
| download | DTMenuDebug.c4d-8088d0e0e3bb948d950e55a3452b39226e6750dd.tar.gz DTMenuDebug.c4d-8088d0e0e3bb948d950e55a3452b39226e6750dd.zip | |
Add submenu-as-column-feature (includes Breaking change of ActivateEntry)
Some fixes
| -rw-r--r-- | Graphics.png | bin | 10647 -> 13307 bytes | |||
| -rw-r--r-- | Script.c | 599 |
2 files changed, 467 insertions, 132 deletions
diff --git a/Graphics.png b/Graphics.png Binary files differindex 737dfed..4b53b19 100644 --- a/Graphics.png +++ b/Graphics.png @@ -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() |
