summaryrefslogtreecommitdiffstats
path: root/DTMenu.c4d
diff options
context:
space:
mode:
authorMarkus Mittendrein <maxmitti@maxmitti.tk>2022-02-13 21:13:36 +0100
committerMarkus Mittendrein <maxmitti@maxmitti.tk>2022-02-13 22:32:44 +0100
commitedd1359010ce32d5ff6a3b77fcb6bbe7e21b5703 (patch)
tree82c00ef38730af6fa4dee2aaafc152922f2e7848 /DTMenu.c4d
parent8bb8f4eea328e75b5a2e17fa82d9392df034e4bc (diff)
downloadObjectsAppend-edd1359010ce32d5ff6a3b77fcb6bbe7e21b5703.tar.gz
ObjectsAppend-edd1359010ce32d5ff6a3b77fcb6bbe7e21b5703.zip
Update to map based DTMenu
Diffstat (limited to 'DTMenu.c4d')
-rw-r--r--DTMenu.c4d/DefCore.txt2
-rw-r--r--DTMenu.c4d/Graphics.pngbin10647 -> 13698 bytes
-rw-r--r--DTMenu.c4d/IconDummy.c4d/DefCore.txt5
-rw-r--r--DTMenu.c4d/IconDummy.c4d/Graphics.png (renamed from DTMenu.c4d/SymbolDummy.c4d/Graphics.png)bin126 -> 126 bytes
-rw-r--r--DTMenu.c4d/IconDummy.c4d/Script.c16
-rw-r--r--DTMenu.c4d/InstantDescriptionDecoration.c4d/ActMap.txt31
-rw-r--r--DTMenu.c4d/InstantDescriptionDecoration.c4d/DefCore.txt9
-rw-r--r--DTMenu.c4d/InstantDescriptionDecoration.c4d/Graphics.pngbin0 -> 10955 bytes
-rw-r--r--DTMenu.c4d/InstantDescriptionDecoration.c4d/Script.c9
-rw-r--r--DTMenu.c4d/Script.c2001
-rw-r--r--DTMenu.c4d/SymbolDummy.c4d/DefCore.txt5
-rw-r--r--DTMenu.c4d/SymbolDummy.c4d/Script.c16
12 files changed, 1422 insertions, 672 deletions
diff --git a/DTMenu.c4d/DefCore.txt b/DTMenu.c4d/DefCore.txt
index bb2b85d..637655d 100644
--- a/DTMenu.c4d/DefCore.txt
+++ b/DTMenu.c4d/DefCore.txt
@@ -1,7 +1,7 @@
[DefCore]
id=MN7I
Name=Menu
-Version=4,9,10,8
+Version=4,9,5
Category=C4D_StaticBack
Width=64
Height=64
diff --git a/DTMenu.c4d/Graphics.png b/DTMenu.c4d/Graphics.png
index 737dfed..a9ad684 100644
--- a/DTMenu.c4d/Graphics.png
+++ b/DTMenu.c4d/Graphics.png
Binary files differ
diff --git a/DTMenu.c4d/IconDummy.c4d/DefCore.txt b/DTMenu.c4d/IconDummy.c4d/DefCore.txt
new file mode 100644
index 0000000..3758e17
--- /dev/null
+++ b/DTMenu.c4d/IconDummy.c4d/DefCore.txt
@@ -0,0 +1,5 @@
+[DefCore]
+id=ID7I
+Name=Menu icon dummy
+Version=4,9,5
+Category=C4D_StaticBack
diff --git a/DTMenu.c4d/SymbolDummy.c4d/Graphics.png b/DTMenu.c4d/IconDummy.c4d/Graphics.png
index 6b1b504..6b1b504 100644
--- a/DTMenu.c4d/SymbolDummy.c4d/Graphics.png
+++ b/DTMenu.c4d/IconDummy.c4d/Graphics.png
Binary files differ
diff --git a/DTMenu.c4d/IconDummy.c4d/Script.c b/DTMenu.c4d/IconDummy.c4d/Script.c
new file mode 100644
index 0000000..58e5f2e
--- /dev/null
+++ b/DTMenu.c4d/IconDummy.c4d/Script.c
@@ -0,0 +1,16 @@
+#strict 2
+
+static const DT_Menu_IconDummy = ID7I;
+
+func SetIcon(id icon, int nr)
+{
+ SetGraphics(0, this, icon);
+ SetPicture(GetDefCoreVal("Picture", "DefCore", icon, 0) + nr * GetDefCoreVal("Picture", "DefCore", icon, 2), GetDefCoreVal("Picture", "DefCore", icon, 1), GetDefCoreVal("Picture", "DefCore", icon, 2), GetDefCoreVal("Picture", "DefCore", icon, 3));
+ return this;
+}
+
+func SetColor(int color)
+{
+ SetClrModulation(color);
+ return this;
+}
diff --git a/DTMenu.c4d/InstantDescriptionDecoration.c4d/ActMap.txt b/DTMenu.c4d/InstantDescriptionDecoration.c4d/ActMap.txt
new file mode 100644
index 0000000..3ab9eca
--- /dev/null
+++ b/DTMenu.c4d/InstantDescriptionDecoration.c4d/ActMap.txt
@@ -0,0 +1,31 @@
+[Action]
+Name=FrameDecoTopLeft
+Facet=0,0,32,28,0,-14
+
+[Action]
+Name=FrameDecoTop
+Facet=32,0,128,28,0,-14
+
+[Action]
+Name=FrameDecoTopRight
+Facet=160,0,32,28,-26,-14
+
+[Action]
+Name=FrameDecoRight
+Facet=190,23,2,1,4,0
+
+[Action]
+Name=FrameDecoBottomRight
+Facet=186,28,6,2,0,0
+
+[Action]
+Name=FrameDecoBottom
+Facet=2,28,128,2,0,0
+
+[Action]
+Name=FrameDecoBottomLeft
+Facet=0,28,2,2,0,0
+
+[Action]
+Name=FrameDecoLeft
+Facet=0,23,2,1,0,0 \ No newline at end of file
diff --git a/DTMenu.c4d/InstantDescriptionDecoration.c4d/DefCore.txt b/DTMenu.c4d/InstantDescriptionDecoration.c4d/DefCore.txt
new file mode 100644
index 0000000..7631f48
--- /dev/null
+++ b/DTMenu.c4d/InstantDescriptionDecoration.c4d/DefCore.txt
@@ -0,0 +1,9 @@
+[DefCore]
+id=DD7I
+Name=DefaultMenuDescriptionDecoration
+Version=4,9,5,4
+Category=1
+MaxUserSelect=0
+Width=1
+Height=1
+Mass=1
diff --git a/DTMenu.c4d/InstantDescriptionDecoration.c4d/Graphics.png b/DTMenu.c4d/InstantDescriptionDecoration.c4d/Graphics.png
new file mode 100644
index 0000000..5241e1c
--- /dev/null
+++ b/DTMenu.c4d/InstantDescriptionDecoration.c4d/Graphics.png
Binary files differ
diff --git a/DTMenu.c4d/InstantDescriptionDecoration.c4d/Script.c b/DTMenu.c4d/InstantDescriptionDecoration.c4d/Script.c
new file mode 100644
index 0000000..1862b57
--- /dev/null
+++ b/DTMenu.c4d/InstantDescriptionDecoration.c4d/Script.c
@@ -0,0 +1,9 @@
+#strict 2
+
+static const DT_Menu_DefaultInstantDescriptionDecoration = DD7I;
+
+func FrameDecorationBackClr() { return 0x5f000000; } // C4GUI_StandardBGColor
+func FrameDecorationBorderTop() { return 14; }
+func FrameDecorationBorderLeft() { return 2; }
+func FrameDecorationBorderRight() { return 4; }
+func FrameDecorationBorderBottom() { return 2; }
diff --git a/DTMenu.c4d/Script.c b/DTMenu.c4d/Script.c
index 35dc576..2089a8d 100644
--- a/DTMenu.c4d/Script.c
+++ b/DTMenu.c4d/Script.c
@@ -1,56 +1,37 @@
-#strict 2
+#strict 3
static const DT_Menu = MN7I;
-static const DT_Menu_Combined = -1;
static const DT_Menu_MenuVar = -1;
// Enums and bitfields
-static const DT_Menu_Settings_Symbol = 0;
-static const DT_Menu_Settings_Object = 1;
-static const DT_Menu_Settings_Extra = 2;
-static const DT_Menu_Settings_Caption = 3;
-static const DT_Menu_Settings_ExtraData = 4;
-static const DT_Menu_Settings_Style = 5;
-static const DT_Menu_Settings_KeepOpen = 6;
-static const DT_Menu_Settings_Parent = 7;
-static const DT_Menu_Settings_Size = 8;
-static const DT_Menu_Settings_RefreshInterval = 9;
-static const DT_Menu_Settings_Selection = 10;
-static const DT_Menu_Settings_ConditionDisableMode = 11;
-static const DT_Menu_Settings_ConditionAllowSelection = 12;
-static const DT_Menu_Settings_Callbacks = 13;
-static const DT_Menu_Settings_Decoration = 14;
-static const DT_Menu_Settings_RequireAction = 15;
-static const DT_Menu_Settings_KeepParentOnClose = 16;
-static const DT_Menu_Settings_Vars = 17;
-static const DT_Menu_Settings_Closable = 18;
-
-static const DT_Menu_KeepOpen_Not = 0x0;
-static const DT_Menu_KeepOpen_Keep = 0x1;
-static const DT_Menu_KeepOpen_Force = 0x2;
-static const DT_Menu_KeepOpen_Permanent = 0x4;
-static const DT_Menu_KeepOpen_Refresh = 0x8;
-static const DT_Menu_KeepOpen_RefreshContinuously = 0x10;
-static const DT_Menu_KeepOpen_Refresh_Mask = 0x18; // DT_Menu_KeepOpen_Refresh | DT_Menu_KeepOpen_RefreshContinuously
-
-static const DT_Menu_Type_Setting = 0;
-static const DT_Menu_Type_Entry = 1;
-static const DT_Menu_Type_Factory = 2;
+static const Menu_KeepOpen_Not = 0x0;
+static const Menu_KeepOpen_Keep = 0x1;
+static const Menu_KeepOpen_Force = 0x2;
+static const Menu_KeepOpen_Permanent = 0x4;
+static const Menu_KeepOpen_Refresh = 0x8;
+static const Menu_KeepOpen_RefreshContinuously = 0x10;
+static const Menu_KeepOpen_Refresh_Mask = 0x18; // Menu_KeepOpen_Refresh | Menu_KeepOpen_RefreshContinuously
+
+static const DT_Menu_Type_Entry = 0;
+static const DT_Menu_Type_Factory = 1;
+static const DT_Menu_Type_Columns = 2;
static const DT_Menu_Action_Normal = 0;
static const DT_Menu_Action_Special2 = 1;
static const DT_Menu_Action_Close = 2;
-static const Menu_React_OverrideReaction = -1;
-static const Menu_React_None = 0;
static const Menu_React_Close = 1;
static const Menu_React_Refresh = 2;
static const Menu_React_KeepOpen = 3;
static const Menu_React_Back = 4;
static const Menu_React_SelectionOffset = 5;
static const Menu_React_SelectionChange = 6;
-static const Menu_React_ShowSubmenu = 7;
+static const Menu_React_ShowSubMenu = 7;
+static const Menu_React_OverrideReaction = -1;
+static Menu_React_None; // constants with nil is somehow broken?
+// static const Menu_React_None = nil;
+static const Menu_React_Max = Menu_React_ShowSubMenu;
global func Menu_React_OffsetSelection(int offset) { return [Menu_React_SelectionOffset, offset]; }
global func Menu_React_OverrideSelection(int override) { return [Menu_React_SelectionChange, override]; }
@@ -61,6 +42,7 @@ static const Menu_ConditionReact_Show = 1;
static const Menu_ConditionReact_Hide = 2;
static const Menu_ConditionReact_GrayOut = 3;
static const Menu_ConditionReact_CustomFormat = 4;
+static const Menu_ConditionReact_Max = Menu_ConditionReact_GrayOut;
global func Menu_ConditionReact_CustomColor(int color) { return [Menu_ConditionReact_CustomFormat, Format("<c %x>%%s</c>", color)]; }
global func Menu_ConditionReact_Format(string format) { return [Menu_ConditionReact_CustomFormat, format]; }
@@ -68,18 +50,7 @@ global func Menu_ConditionReact_Format(string format) { return [Menu_ConditionRe
static const Menu_Condition_Default = 0;
static const Menu_Condition_AllowSelection = 1;
static const Menu_Condition_DenySelection = 2;
-
-static const DT_Menu_Entry_Caption = 0;
-static const DT_Menu_Entry_Callbacks = 1;
-static const DT_Menu_Entry_Symbol = 2;
-static const DT_Menu_Entry_Count = 3;
-static const DT_Menu_Entry_InfoCaption = 4;
-static const DT_Menu_Entry_Extra = 5;
-static const DT_Menu_Entry_XPar1 = 6;
-static const DT_Menu_Entry_XPar2 = 7;
-static const DT_Menu_Entry_Args = 8;
-static const DT_Menu_Entry_Condition = 9;
-static const DT_Menu_Entry_Placeholder = 10;
+static const Menu_Condition_Max = Menu_Condition_DenySelection;
static const Menu_CallbackType_None = 0x0;
static const Menu_CallbackType_Special2 = 0x1;
@@ -92,57 +63,48 @@ static const Menu_CallbackType_InputAborted = 0x40;
static const Menu_CallbackType_Defaults = 0x3; // Menu_CallbackType_Normal | Menu_CallbackType_Special2
static const Menu_CallbackType_All = 0x7f; // Menu_CallbackType_Normal | Menu_CallbackType_Special2 | Menu_CallbackType_Close | Menu_CallbackType_Selection | Menu_CallbackType_Deselection | Menu_CallbackType_ValueChanged | Menu_CallbackType_InputAborted
-static const Menu_AdaptorType_Boolean = 0;
-static const Menu_AdaptorType_Integer = 1;
-static const Menu_AdaptorType_String = 2;
-static const Menu_AdaptorType_ID = 3;
-static const Menu_AdaptorType_Enum = 4;
-static const Menu_AdaptorType_BitField = 5;
+static const Menu_AdaptorType_Boolean = 1;
+static const Menu_AdaptorType_Integer = 2;
+static const Menu_AdaptorType_String = 3;
+static const Menu_AdaptorType_ID = 4;
+static const Menu_AdaptorType_Enum = 5;
+static const Menu_AdaptorType_BitField = 6;
static const Menu_Adaptor_Limits_Max = 0x7fffffff;
static const Menu_Adaptor_Limits_Min = 0x80000000;
static const Menu_CallbackArg_All = -1;
static const Menu_CallbackArg_Action = 0;
-static const Menu_CallbackArg_Symbol = 1;
+static const Menu_CallbackArg_Icon = 1;
static const Menu_CallbackArg_MenuObject = 2;
static const Menu_CallbackArg_Args = 3;
static const Menu_CallbackArg_NewSelection = 4;
static const Menu_CallbackArg_OldSelection = 5;
static const Menu_CallbackArg_NewValue = 6;
static const Menu_CallbackArg_OldValue = 7;
-static const Menu_CallbackArg_FromSubmenu = 8;
+static const Menu_CallbackArg_FromSubMenu = 8;
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;
-static const DT_Menu_Adaptor_Type = 0;
-static const DT_Menu_Adaptor_Variable = 1;
-static const DT_Menu_Adaptor_Callbacks = 2;
-static const DT_Menu_Adaptor_MessageBoardText = 3;
-static const DT_Menu_Adaptor_Limits = 4;
-static const DT_Menu_Adaptor_StepSize = 5;
-static const DT_Menu_Adaptor_LayoutVals = 6;
-static const DT_Menu_Adaptor_NoEmptyString = 7;
-static const DT_Menu_Adaptor_EntryIndex = 8;
-static const DT_Menu_Adaptor_Mask = 9;
-static const DT_Menu_Adaptor_WrapAround = 10;
-static const DT_Menu_Adaptor_Args = 11;
-static const DT_Menu_Adaptor_EnumSubmenu = 12;
-static const DT_Menu_Adaptor_EnumSubmenuCaption = 13;
-static const DT_Menu_Adaptor_EnumSubmenuSymbol = 14;
-static const DT_Menu_Adaptor_EnumAllowUnknown = 15;
-static const DT_Menu_Adaptor_EnumInline = 16;
-
-static const Menu_Layout_Symbol = 1;
-static const Menu_Layout_InfoCaption = 2;
-
-static const Menu_Layout__CaptionPos = 3;
+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;
+
+static const Menu_Layout__TextPos = 3;
static const Menu_Layout__ValuePos = 4;
// static const Menu_Layout__InputValuePos = 5; TODO
-static const Menu_Layout_Caption = 4;
+static const Menu_Layout_Text = 4;
static const Menu_Layout_Value = 8;
// static const Menu_Layout_InputValue = 16; TODO
static const Menu_Layout__NoFlagMask = 3;
@@ -150,21 +112,30 @@ 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 shouldChangeColumn;
local suspended;
local closing;
+local refreshing;
local msgBoardMode;
local msgBoardEntry;
local noSelectionCallbacks;
+local multiColumnMode;
local subMenu;
local vars;
func Initialize()
{
+ currentColumnSelections = [-1];
+ columnEntries = [];
+ msgBoardEntry = [];
vars = [];
+ currentColumn = 0;
}
func Destruction()
@@ -172,18 +143,24 @@ func Destruction()
Close();
}
-func Create(array cSettings, array cEntries)
+func Create(map cSettings)
{
- settings = cSettings;
- entries = [];
- createEntries = cEntries;
- currentSelection = -1;
- settings[DT_Menu_Settings_ConditionDisableMode] = settings[DT_Menu_Settings_ConditionDisableMode] || Menu_ConditionReact_Hide;
- entryCount = 0;
+ settings = cSettings.Settings;
+ createEntries = cSettings.Entries;
+
+ multiColumnMode = false;
+ columnCount = 0;
- if(settings[DT_Menu_Settings_Vars])
+ var actualColumnCount = GetLength(currentColumnSelections);
+ columnOffsets = CreateArray(actualColumnCount);
+ for(var i = 0; i < actualColumnCount; ++i)
{
- var settingVars = settings[DT_Menu_Settings_Vars];
+ columnOffsets[i] = 0;
+ }
+
+ if(settings.Vars)
+ {
+ var settingVars = settings.Vars;
for(var i = 0; i < GetLength(settingVars); ++i)
{
@@ -192,54 +169,165 @@ func Create(array cSettings, array cEntries)
vars[i] = settingVars[i];
}
}
- settings[DT_Menu_Settings_Vars] = 0;
+ settings.Vars = 0;
}
var factoryArgs = [];
factoryArgs[Menu_CallbackArg_Menu] = this;
- factoryArgs[Menu_CallbackArg_MenuObject] = settings[DT_Menu_Settings_Object];
+ factoryArgs[Menu_CallbackArg_MenuObject] = settings.Object;
+
+ var index = 0;
+ columnEntries[0] = [];
+ HandleEntries(createEntries, index, columnEntries[0], settings, factoryArgs, 0, true);
+
+ if(columnCount > 1 && settings.Style != C4MN_Style_Context)
+ {
+ FatalError("CreateNewMenu: Multi column menus (and submenus as columns) are only supported with Menu_Style_Context()");
+ }
- HandleEntries(createEntries, entryCount, entries, factoryArgs);
+ if(settings.Parent) settings.Parent->Suspend();
+
+ if(!(settings.Object)) { FatalError("Assertion failed: CreateNewMenu: menu object is missing; assertion code: settings.Object"); }
+ var title = settings.Title;
+
+ if(!multiColumnMode)
+ {
+ var submenuTitle;
+ // update submenu columns if necessary and build title
+ for(var i = 0; i < GetLength(currentColumnSelections); ++i)
+ {
+ // TODO: cascade? then remove the columnSelection == -1 check; and check if shrinking menus still work correctly
+ if(!columnEntries[i])
+ {
+ currentColumnSelections[i] = -1;
+ break;
+ }
+ var columnSelection = currentColumnSelections[i];
+ if((columnSelection == -1) || columnSelection >= GetLength(columnEntries[i]))
+ {
+ currentColumnSelections[i] = GetLength(columnEntries[i]) - 1;
+ break;
+ }
+
+ var submenuEntries = columnEntries[i][columnSelection].SubMenuColumnEntries;
+ if(submenuEntries)
+ {
+ columnEntries[i + 1] = submenuEntries;
+
+ var submenuSettings = columnEntries[i][columnSelection].SubMenuInitColumnData.Settings;
+ /*var */submenuTitle = submenuSettings.Title || "";
+ if(submenuSettings.Icon)
+ {
+ submenuTitle = Format("{{%i}} %s", submenuSettings.Icon, submenuTitle);
+ }
- if(settings[DT_Menu_Settings_Parent]) settings[DT_Menu_Settings_Parent]->Suspend();
+ // because of the engine such long titles are not nice; maybe do "... > last SubMenu Title"
+ // the engine makes entry columns at least as long as the title is...
+ // title = Format("%s {{MN7I:7}} %s", title, submenuTitle);
+ }
+ }
- CreateMenu(settings[DT_Menu_Settings_Symbol], settings[DT_Menu_Settings_Object], this, settings[DT_Menu_Settings_Extra], settings[DT_Menu_Settings_Caption], settings[DT_Menu_Settings_ExtraData], settings[DT_Menu_Settings_Style], true, MN7I);
+ if(submenuTitle)
+ {
+ title = Format("... {{MN7I:7}} %s", submenuTitle);
+ }
+ }
- 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]);
+ CreateMenu(settings.Icon, settings.Object, this, settings.Extra, title, settings.ExtraData, settings.Style, true, MN7I);
+ if(settings.Size) SetMenuSize(settings.Size[0], settings.Size[1], settings.Object);
- AddEntries(entries);
+ AddEntries();
- if(entryCount > 0)
+ if(GetLength(columnEntries[0]) > 0)
+ {
+ if(!refreshing)
+ {
+ SelectEntry(settings.Selection[0], settings.Selection[1]);
+ }
+ }
+ else
{
- SelectEntry(settings[DT_Menu_Settings_Selection]);
+ Log("WARNING: DT_Menu::Create: Created an empty menu");
}
- if(settings[DT_Menu_Settings_Decoration])
+ if(settings.Decoration)
{
- SetMenuDecoration(settings[DT_Menu_Settings_Decoration], settings[DT_Menu_Settings_Object]);
+ SetMenuDecoration(settings.Decoration, settings.Object);
}
if(!GetEffect("Menu", this)) AddEffect("Menu", this, 1, 1, this, 0);
}
-func SelectEntry(int selection)
+func LeaveSubMenuColumn()
{
- if(selection < 0) selection = entryCount + selection;
- SelectMenuItem(selection, settings[DT_Menu_Settings_Object]);
+ if(!(!multiColumnMode && currentColumn > 0)) { FatalError("Assertion failed: LeaveSubMenuColumn: currentColumn is not a submenu; can't leave; assertion code: !multiColumnMode && currentColumn > 0"); }
+ SelectEntry(currentColumnSelections[currentColumn - 1], currentColumn - 1);
+ Refresh(GetSelection(Menu_Selection_WithColumn));
}
-func ActivateEntry(int index, int action)
+func SelectEntry(indexOrChain, int column)
{
- if(index < 0) index = entryCount + index;
- if(index >= entryCount || index < 0)
+ if(GetType(indexOrChain) == C4V_Array)
{
- return;
+ 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)
+ {
+ column ??= 0;
+ 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"); }
+ indexOrChain ??= 0;
+ 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"); }
+ shouldChangeColumn = true;
+ SelectMenuItem(EncodeSelection(indexOrChain, column), settings.Object);
+ shouldChangeColumn = false;
+ }
+ else
+ {
+ FatalError(Format("DT_Menu::SelectEntry: indexOrChain must be an integer or an array of integers but got %d", GetType(indexOrChain)));
+ }
+}
- SelectEntry(index);
+func SelectTopEntry(int column)
+{
+ for(var i = 0; i < GetLength(columnEntries[column]); ++i)
+ {
+ if(columnEntries[column][i].Placeholder)
+ {
+ return SelectEntry(i, column);
+ }
+ }
+}
- var entry = entries[index];
- MenuItemCommand(entry[DT_Menu_Entry_Symbol], index, action);
+func SelectBottomEntry(int column)
+{
+ for(var i = GetLength(columnEntries[column]) - 1; i >= 0; --i)
+ {
+ if(columnEntries[column][i].Placeholder)
+ {
+ return SelectEntry(i, column);
+ }
+ }
+}
+
+func ActivateEntry(int action, indexOrChain, int column)
+{
+ SelectEntry(indexOrChain);
+
+ var index = indexOrChain;
+ if(GetType(indexOrChain) == C4V_Array)
+ {
+ column = GetLength(indexOrChain) - 1;
+ index = indexOrChain[column];
+ }
+
+ var entry = columnEntries[column][index];
+ MenuItemCommand(entry.Icon, EncodeSelection(index, column), action);
}
func SubMenu()
@@ -264,14 +352,14 @@ global func MenuVar(int index)
func FxMenuTimer(object target, int effectNumber, int effectTime)
{
- if(!settings[DT_Menu_Settings_Object])
+ if(!settings.Object)
{
return Close();
}
- else if(settings[DT_Menu_Settings_RequireAction])
+ else if(settings.RequireAction)
{
- var obj = settings[DT_Menu_Settings_Object];
- var requirement = settings[DT_Menu_Settings_RequireAction];
+ var obj = settings.Object;
+ var requirement = settings.RequireAction;
if(GetAction(obj) != requirement[0] || (requirement[1] && GetActionTarget(0, obj) != requirement[1]))
{
return Close();
@@ -280,23 +368,23 @@ func FxMenuTimer(object target, int effectNumber, int effectTime)
if(suspended) return;
- if(msgBoardMode != 0 && !TestMessageBoard(GetOwner(settings[DT_Menu_Settings_Object]), true))
+ if(msgBoardMode && !TestMessageBoard(GetOwner(settings.Object), true))
{
- var entry = entries[msgBoardEntry];
- 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_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]);
+ var entry = columnEntries[msgBoardEntry[1]][msgBoardEntry[0]];
+ var args = entry.Args;
+ var reaction = CallCallbacks(args?.Callbacks, Menu_CallbackType_InputAborted, [Menu_CallbackType_InputAborted, entry.Icon, settings.Object, entry.Args]);
if(reaction != Menu_React_None)
{
React(reaction, msgBoardEntry);
}
- msgBoardMode = 0;
+ msgBoardMode = nil;
}
- if(!GetMenu(settings[DT_Menu_Settings_Object]))
+ if(!GetMenu(settings.Object))
{
- if(settings[DT_Menu_Settings_KeepOpen] & (DT_Menu_KeepOpen_Refresh_Mask | DT_Menu_KeepOpen_Force) && !settings[DT_Menu_Settings_Closable])
+ if(settings.KeepOpen & (Menu_KeepOpen_Refresh_Mask | Menu_KeepOpen_Force) && !settings.Closable)
{
- Refresh(currentSelection);
+ Refresh(GetSelection(Menu_Selection_WithColumn));
}
else
{
@@ -304,9 +392,9 @@ func FxMenuTimer(object target, int effectNumber, int effectTime)
}
}
- if(settings[DT_Menu_Settings_KeepOpen] & DT_Menu_KeepOpen_RefreshContinuously && !(effectTime % settings[DT_Menu_Settings_RefreshInterval]))
+ if(settings.KeepOpen & Menu_KeepOpen_RefreshContinuously && !(effectTime % settings.RefreshInterval))
{
- Refresh(currentSelection);
+ Refresh(GetSelection(Menu_Selection_WithColumn));
}
}
@@ -316,8 +404,9 @@ func FxMenuStop(object target, int effectNumber, int reason, bool temp)
{
return;
}
- CloseMenu(settings[DT_Menu_Settings_Object]);
- if(settings[DT_Menu_Settings_Parent]) settings[DT_Menu_Settings_Parent]->Suspend(true);
+ CloseMenu(settings.Object);
+ if(settings.InstantDescription.Enable) HideInstantDescription();
+ if(settings.Parent) settings.Parent->Suspend(true);
RemoveObject(this);
}
@@ -359,14 +448,20 @@ func CallCallbacks(array callbacks, int type, array args, defaultRet, bool noGlo
}
}
+ if(!(!ret || (GetType(ret) == C4V_Int && ret > 0 && ret <= Menu_React_Max) || (GetType(ret) == C4V_Array && ret[0] > 0 && ret[0] <= Menu_React_Max && (!ret[1] || GetType(ret[1]) == C4V_Int)))) { FatalError(Format("Assertion failed: CallCallbacks: invalid return value detected: %v; assertion code: !ret || (GetType(ret) == C4V_Int && ret > 0 && ret <= Menu_React_Max) || (GetType(ret) == C4V_Array && ret[0] > 0 && ret[0] <= Menu_React_Max && (!ret[1] || GetType(ret[1]) == C4V_Int))", ret)); }
if(!noGlobalCallbacks)
{
args[Menu_CallbackArg_Returned] = ret;
- var globalRet = CallCallbacks(settings[DT_Menu_Settings_Callbacks], type, args, defaultRet, true);
+ var globalRet = CallCallbacks(settings.Callbacks, type, args, defaultRet, true);
if(GetType(globalRet) == C4V_Array && globalRet[0] == Menu_React_OverrideReaction)
{
ret = globalRet[1];
+ if(!(!ret || (GetType(ret) == C4V_Int && ret > 0 && ret <= Menu_React_Max) || (GetType(ret) == C4V_Array && ret[0] > 0 && ret[0] <= Menu_React_Max && (!ret[1] || GetType(ret[1]) == C4V_Int)))) { FatalError(Format("Assertion failed: CallCallbacks: invalid return value detected: %v; assertion code: !ret || (GetType(ret) == C4V_Int && ret > 0 && ret <= Menu_React_Max) || (GetType(ret) == C4V_Array && ret[0] > 0 && ret[0] <= Menu_React_Max && (!ret[1] || GetType(ret[1]) == C4V_Int))", ret)); }
+ }
+ else if(globalRet)
+ {
+ Log("WARNING: DT_Menu::CallCallbacks: ignoring non-zero return value of global callback because override is not specified: %v", globalRet);
}
}
@@ -380,7 +475,7 @@ func Close(bool closeParents)
{
subMenu->Close();
}
- if(closeParents && settings[DT_Menu_Settings_Parent]) settings[DT_Menu_Settings_Parent]->Close(true);
+ if(closeParents && settings.Parent) settings.Parent->Close(true);
RemoveEffect("Menu", this);
}
@@ -389,173 +484,343 @@ func Suspend(bool cont)
if(suspended == !cont) return;
if(suspended = !cont)
{
- CloseMenu(settings[DT_Menu_Settings_Object]);
+ CloseMenu(settings.Object);
}
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, map& retSettings, array& factoryArgs, int column, bool isMainColumn)
{
+ if(column >= columnCount)
+ {
+ columnCount = column + 1;
+ }
+
for(var entry in factoryEntries)
{
- if(entry[0] == DT_Menu_Type_Entry)
+ if(!(GetType(entry) == C4V_Map)) { FatalError(Format("Assertion failed: \"DT_Menu::HandleEntries: invalid entry. must be a map, got %v\"; assertion code: GetType(entry) == C4V_Map", entry)); }
+ if(entry.Type == DT_Menu_Type_Entry)
{
- ret[i++] = entry[1];
+ if(multiColumnMode && isMainColumn)
+ {
+ FatalError("DT_Menu::HandleEntries: if Menu_Columns() is used, all entries must occur inside it");
+ }
+ // is it an extra column submenu?
+ if(entry.SubMenuInitColumnData)
+ {
+ if(multiColumnMode)
+ {
+ FatalError("DT_Menu::HandleEntries: Menu_Columns() and SubMenu columns can not be used together");
+ }
+
+ if(!entry.SubMenuInitColumnData.Settings)
+ {
+ entry.SubMenuInitColumnData.Settings = {};
+ }
+
+ var index = 0;
+ HandleEntries(entry.SubMenuInitColumnData.Entries, index, entry.SubMenuColumnEntries = [], entry.SubMenuInitColumnData.Settings, factoryArgs, column + 1, false);
+ }
+
+ ret[i++] = entry;
}
- else if(entry[0] == DT_Menu_Type_Factory)
+ else if(entry.Type == DT_Menu_Type_Factory)
{
- factoryArgs[Menu_CallbackArg_Args] = entry[1][1];
-
- for(var callback in entry[1][0])
+ for(var callback in entry.Callbacks)
{
+ factoryArgs[Menu_CallbackArg_Args] = entry.Args;
factoryArgs[Menu_CallbackArg_EntryNumber] = i;
- var factoryResult = CallA(callback, BindCallbackArgs(factoryArgs, entry[1][2]));
- if(GetType(factoryResult) == C4V_Array)
+ factoryArgs[Menu_CallbackArg_EntryColumn] = column;
+ var factoryResult = CallA(callback, BindCallbackArgs(factoryArgs, entry.Binding));
+
+ if(factoryResult == Menu_React_Close)
{
- var newEntries = [];
- UncombineAndDistinguish(factoryResult, settings, newEntries);
- HandleEntries(newEntries, i, ret, factoryArgs);
+ return Close();
}
- else if(factoryResult == Menu_React_Close)
+ else
{
- return Close();
+ var newSettings;
+ var newEntries;
+ if(GetType(factoryResult) == C4V_Map)
+ {
+ newSettings = factoryResult.Settings;
+ newEntries = factoryResult.Entries;
+ }
+ else if(GetType(factoryResult) == C4V_Array)
+ {
+ newEntries = factoryResult;
+ }
+ else
+ {
+ Log("WARNING: DT_Menu::HandleEntries: ignoring invalid return value of Factory: %v (%v)", factoryResult, callback);
+ }
+
+ if(newSettings)
+ {
+ retSettings = Extend(retSettings, newSettings, true);
+ }
+ if(newEntries)
+ {
+ HandleEntries(newEntries, i, ret, retSettings, factoryArgs, column, isMainColumn);
+ }
}
- }
+ }
}
- }
-}
-
-func AddEntries(array& entries)
-{
- for(var i = 0; i < GetLength(entries); ++i)
- {
- var entry = entries[i];
- var condition = entry[DT_Menu_Entry_Condition], conditionRet;
- var caption = entry[DT_Menu_Entry_Caption], noCommand = !entry[DT_Menu_Entry_Placeholder] || (entry[DT_Menu_Entry_Placeholder] != true && !entry[DT_Menu_Entry_Callbacks]);
- if(condition)
+ else if(entry.Type == DT_Menu_Type_Columns)
{
- if(noCommand || condition[1] == Menu_Condition_DenySelection || (condition[1] == Menu_Condition_Default && !settings[DT_Menu_Settings_ConditionAllowSelection]))
+ if(multiColumnMode)
{
- noCommand = true;
+ FatalError("DT_Menu::HandleEntries: only one Menu_Columns() can appear in the whole menu");
}
- conditionRet = CallA(condition[0], [entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]) || settings[DT_Menu_Settings_ConditionDisableMode];
- if(conditionRet == Menu_ConditionReact_Hide)
+ if(columnCount > 1)
{
- continue;
+ FatalError("DT_Menu::HandleEntries: Menu_Columns() and SubMenu columns can not be used together");
}
- else if(conditionRet == Menu_ConditionReact_GrayOut)
+
+ if(GetLength(columnEntries[0]) > 0)
{
- caption = Format("<c 808080>%s</c>", caption);
+ FatalError("DT_Menu::HandleEntries: if Menu_Columns() is used, all entries must occur inside it");
}
- else if(GetType(conditionRet) == C4V_Array && conditionRet[0] == Menu_ConditionReact_CustomFormat)
+
+ multiColumnMode = true;
+
+ var j = 0;
+ for(var columnData in entry.Columns)
{
- caption = Format(conditionRet[1], caption);
+ columnEntries[j] = [];
+
+ var index = 0;
+ var tempSettings = {};
+ // NOTE: First instead of tempSettings, settings was used. Seems wrong.
+ HandleEntries(columnData, index, columnEntries[j], tempSettings, factoryArgs, j, false);
+ ++j;
+
+ if(GetLength(tempSettings) > 0)
+ {
+ FatalError("DT_Menu::HandleEntries: menu settings can not appear in Menu_Columns()");
+ }
}
- else
+ }
+ else
+ {
+ Log("WARNING: DT_Menu::HandleEntries: ignoring invalid Non-Entry/Factory: %v", entry);
+ }
+ }
+}
+
+func AddEntries()
+{
+ // 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)
+ {
+ if(column)
+ {
+ entriesCount = Max(entriesCount, GetLength(column));
+ }
+ }
+
+ if(columnCount > 1)
+ {
+ SetMenuSize(columnCount, entriesCount, settings.Object);
+ }
+
+ if(!multiColumnMode)
+ {
+ for(var i = 1; i < GetLength(columnEntries); ++i)
+ {
+ var column = columnEntries[i];
+ if(column)
{
- noCommand = false;
+ var offset = currentColumnSelections[i - 1] + columnOffsets[i - 1];
+ var entryCount = GetLength(column);
+ if(offset + entryCount > entriesCount)
+ {
+ offset = entriesCount - entryCount;
+ }
+ columnOffsets[i] = offset;
}
}
+ }
- var symbol = entry[DT_Menu_Entry_Symbol], symbolID = 0, deleteSymbol = 0;
- if(GetType(symbol) == C4V_Array)
+ for(var i = 0, entryIndex = 0; i < entriesCount; ++i)
+ {
+ for(var j = 0; j < columnCount; ++j)
{
- if(GetType(symbol[0]) == C4V_C4ID)
+ columnOffsets[j] ??= 0;
+ var rowIndex = i - columnOffsets[j];
+ var entries = columnEntries[j];
+ if(entries && rowIndex >= 0 && rowIndex < GetLength(entries))
{
- symbolID = symbol[0];
- if(symbol[2])
+ var entry = entries[rowIndex];
+ var condition = entry.Condition, conditionRet;
+ var text = entry.Text, noCommand = !entry.Placeholder || (entry.Placeholder != true && !entry.Callbacks);
+ if(condition)
{
- symbol = [CreateSymbolDummy()->SetSymbol(symbolID, symbol[1])->SetColor(symbol[2]), true];
+ if(noCommand || condition.AllowDisabledSelection == Menu_Condition_DenySelection || (condition.AllowDisabledSelection == Menu_Condition_Default && !settings.Condition.AllowSelection))
+ {
+ noCommand = true;
+ }
+
+ conditionRet = CallA(condition.Callback, [entry.Icon, settings.Object, entry.Args]) || settings.Condition.DisableMode;
+ 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(symbol[1])
+
+ var icon = entry.Icon, iconIndex = 0, iconID = 0, deleteIcon = 0;
+ if(GetType(icon) == C4V_Array)
{
- entry[DT_Menu_Entry_Extra] |= C4MN_Add_ImgIndexed;
- entry[DT_Menu_Entry_XPar1] = symbol[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.Extra |= C4MN_Add_ImgIndexed;
+ iconIndex = icon[1];
+ entry.XPar1 = icon[1];
+ }
+ }
+
+ if(GetType(icon[0]) == C4V_C4Object)
+ {
+ entry.Extra |= C4MN_Add_ImgObject;
+ entry.XPar1 = icon[0];
+ if(icon[1])
+ {
+ deleteIcon = icon[0];
+ }
+ if(!iconID)
+ {
+ iconID = GetID(icon[0]);
+ }
+ }
}
- }
+ else
+ {
+ iconID = icon;
+ }
+ entry.Icon = iconID;
- if(GetType(symbol[0]) == C4V_C4Object)
- {
- entry[DT_Menu_Entry_Extra] |= C4MN_Add_ImgObject;
- entry[DT_Menu_Entry_XPar1] = symbol[0];
- deleteSymbol = symbol[1];
- symbolID = GetID(symbol[0]);
- }
- }
- else
- {
- symbolID = symbol;
- }
- entry[DT_Menu_Entry_Symbol] = symbolID;
+ if(!entry.InstantDescriptionIcon)
+ {
+ entry.InstantDescriptionIcon = [iconID, iconIndex];
+ }
- entries[i] = entry;
+ if(settings.InstantDescription.Enable)
+ {
+ entry.Extra |= C4MN_Add_ForceNoDesc;
+ }
- AddMenuItem(caption, !noCommand && "MenuItemCommand", symbolID, settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Count], i, entry[DT_Menu_Entry_InfoCaption], entry[DT_Menu_Entry_Extra], entry[DT_Menu_Entry_XPar1], entry[DT_Menu_Entry_XPar2]);
+ entry.Placeholder = !noCommand;
- if(deleteSymbol)
- {
- RemoveObject(deleteSymbol);
+ columnEntries[j][rowIndex] = entry;
+ var showDesc = !(entry.Extra & C4MN_Add_ForceNoDesc);
+
+ if(entry.SubMenuInitColumnData && currentColumnSelections[j] == rowIndex)
+ {
+ text = Format("%s {{MN7I:7}}", text);
+ }
+
+ AddMenuItem(text, !noCommand && "MenuItemCommand", iconID, settings.Object, entry.Count, entryIndex, showDesc && entry.Description || nil, entry.Extra, entry.XPar1, entry.XPar2);
+
+ if(deleteIcon)
+ {
+ RemoveObject(deleteIcon);
+ }
+ }
+ else
+ {
+ AddMenuItem("", "DummyItemCommand", nil, settings.Object, nil, entryIndex, nil, C4MN_Add_ForceNoDesc);
+ }
+ ++entryIndex;
}
}
}
-func React(reaction, int itemNumber, int refreshDelayed)
+func React(reaction, array entryIndex, int refreshDelayed)
{
if(reaction == Menu_React_Close)
{
- Close(!settings[DT_Menu_Settings_KeepParentOnClose]);
+ Close(!settings.KeepParentOnClose);
}
else if(reaction == Menu_React_Back)
{
- Close();
+ if(currentColumn > 0 && !multiColumnMode)
+ {
+ LeaveSubMenuColumn();
+ }
+ else
+ {
+ Close();
+ }
}
- else if(reaction == Menu_React_Refresh || (settings[DT_Menu_Settings_KeepOpen] & DT_Menu_KeepOpen_Refresh))
+ else if(reaction == Menu_React_Refresh || (settings.KeepOpen & 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] %= GetLength(columnEntries[currentColumn]);
}
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]);
}
}
}
-func CheckCondition(array entry)
+func CheckCondition(map entry)
{
- var condition = entry[DT_Menu_Entry_Condition];
- return !condition || (CallA(condition[0], [entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]) || settings[DT_Menu_Settings_ConditionDisableMode]) == Menu_ConditionReact_Show;
+ var condition = entry.Condition;
+ return !condition || (CallA(condition.Callback, [entry.Icon, settings.Object, entry.Args]) || settings.Condition.DisableMode) == Menu_ConditionReact_Show;
}
func MenuItemCommand(id ID, int itemNumber, int action)
{
- var entry = entries[itemNumber];
- var condition = entry[DT_Menu_Entry_Condition];
+ var column = DecodeSelection(itemNumber);
+ var entry = columnEntries[column][itemNumber];
+ var condition = entry.Condition;
action = action || Menu_CallbackType_Normal;
var reaction;
if(CheckCondition(entry))
{
- reaction = CallCallbacks(entry[DT_Menu_Entry_Callbacks], action, [action, ID, settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]);
+ reaction = CallCallbacks(entry.Callbacks, action, [action, ID, settings.Object, entry.Args]);
}
else
{
- if(condition[1] == Menu_Condition_AllowSelection)
+ if(condition.AllowDisabledSelection == Menu_Condition_AllowSelection)
{
reaction = Menu_React_KeepOpen;
}
@@ -564,17 +829,33 @@ func MenuItemCommand(id ID, int itemNumber, int action)
reaction = Menu_React_Refresh;
}
}
- React(reaction, itemNumber);
+ React(reaction, [itemNumber, column]);
}
-func SubmenuItemCallback(int action, object menuObject, args, array allArgs)
+// 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)
{
allArgs[Menu_CallbackArg_Args] = args[1];
var reaction = CallCallbacks(args[0], action, allArgs, Menu_React_None);
- if(((action & Menu_CallbackType_Defaults) && reaction == Menu_React_None) || reaction == Menu_React_ShowSubmenu)
+ if(((action & Menu_CallbackType_Defaults) && reaction == Menu_React_None) || reaction == Menu_React_ShowSubMenu)
{
- subMenu = CreateNewMenu(args[2], settings, this);
+ if(args[3])
+ {
+ var targetSelection = columnEntries[currentColumn][currentColumnSelections[currentColumn]].SubMenuInitColumnData.Settings.Selection;
+ SelectEntry(targetSelection && targetSelection[0], currentColumn + 1);
+ }
+ else
+ {
+ subMenu = CreateNewMenu(args[2], settings, this);
+ }
return Menu_React_None;
}
else
@@ -583,21 +864,35 @@ 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_Symbol], settings[DT_Menu_Settings_Object], entry[DT_Menu_Entry_Args]]);
+ reaction = CallCallbacks(entry.Callbacks, Menu_CallbackType_Close, [Menu_CallbackType_Close, entry.Icon, settings.Object, 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))
+ if((settings.KeepOpen != Menu_KeepOpen_Not && settings.KeepOpen != Menu_KeepOpen_Permanent && !settings.Closable) || (reaction == Menu_React_KeepOpen))
{
return true;
}
@@ -605,37 +900,208 @@ func MenuQueryCancel(int selection, object menuObject)
func OnMenuSelection(int selection, object menuObject)
{
- 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
+ // TODO: Fix stack overflow when the target selection can't be selected (Placeholder)
+ 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_Symbol], 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_Symbol], 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
+ SelectBottomEntry(column);
+ }
+ else
+ {
+ // otherwise maybe wrapped around from the bottom or just hovered it with the mouse
+ SelectTopEntry(column);
+ }
+ return;
}
+ skipHandling = true;
+ }
- if(deselectReaction != Menu_React_None)
+ if(selection >= GetLength(columnEntries[column]))
+ {
+ if(currentColumn == column)
+ {
+ if(oldColumnSelection == GetLength(columnEntries[column]) - 1)
+ {
+ // wrap around if the last selection was the bottom already
+ SelectTopEntry(column);
+ }
+ else
+ {
+ // otherwise maybe wrapped around from the top or just hovered it with the mouse
+ SelectBottomEntry(column);
+ }
+ return;
+ }
+ else if(multiColumnMode)
{
- React(deselectReaction, currentSelection);
+ SelectBottomEntry(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.Icon, settings.Object, entry.Args, selection, oldColumnSelection];
+ args[Menu_CallbackArg_NewSelectionColumn] = currentColumn;
+ args[Menu_CallbackArg_OldSelectionColumn] = oldColumn;
+ deselectReaction = CallCallbacks(entry.Callbacks, Menu_CallbackType_Deselection, args);
+ }
+
+ var oldIsColumnSubMenu = !!entry.SubMenuInitColumnData;
+
+ currentColumnSelections[column] = selection;
+ entry = columnEntries[column][selection];
+
+ if(!noSelectionCallbacks && CheckCondition(entry))
+ {
+ var args = [Menu_CallbackType_Selection, entry.Icon, settings.Object, entry.Args, selection, oldColumnSelection];
+ args[Menu_CallbackArg_NewSelectionColumn] = currentColumn;
+ args[Menu_CallbackArg_OldSelectionColumn] = oldColumn;
+ selectReaction = CallCallbacks(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];
+ }
+
+ // navigating between submenu columns; not in multiColumnMode
+ if(!multiColumnMode)
+ {
+ // navigated from one column to another without going over the left/right border
+ if(selection + columnOffsets[currentColumn] == oldColumnOldSelection + columnOffsets[oldColumn])
+ {
+ if(currentColumn == oldColumn + 1 && oldIsColumnSubMenu)
+ {
+ var targetSelection = columnEntries[currentColumn - 1][oldColumnOldSelection].SubMenuInitColumnData.Settings.Selection;
+ targetSelection = targetSelection && targetSelection[0];
+ // column submenu was entered through pressing right; select the first entry
+ // but only if it's not there yet
+ if(selection != targetSelection)
+ {
+ SelectEntry(targetSelection, 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;
+ }
+ }
+ }
+ else if(currentColumn != oldColumn && !shouldChangeColumn) // with going over the left/right border; we don’t want that
+ {
+ currentColumn = oldColumn;
+ SelectEntry(oldColumnOldSelection, oldColumn);
+ return;
+ }
}
- if(selectReaction != Menu_React_None)
+ if(skipHandling)
{
- React(selectReaction, currentSelection);
+ currentColumn = oldColumn;
+ return;
+ }
+
+ if(settings.InstantDescription.Enable)
+ {
+ ShowInstantDescription(columnEntries[currentColumn][selection]);
+ }
+
+ // update submenu column if necessary
+ if(!multiColumnMode && selection != oldColumnSelection)
+ {
+ if(entry.SubMenuInitColumnData)
+ {
+ 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];
+ if (oldEntries != nil)
+ {
+ 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)
{
@@ -648,102 +1114,80 @@ func Refresh(int selection, bool delayed)
}
else
{
- var disabledCallbacks;
- if(!noSelectionCallbacks)
+ var oldNoSelectionCallbacks = noSelectionCallbacks;
+ noSelectionCallbacks = true;
+
+ CloseMenu(settings.Object);
+
+ var oldRefreshing = refreshing;
+ refreshing = true;
+ Create({ Settings = settings, Entries = createEntries });
+ refreshing = oldRefreshing;
+ var column = BoundBy(selection[1], 0, columnCount);
+ selection = selection[0];
+
+ if(GetLength(columnEntries[column]) > 0)
{
- disabledCallbacks = noSelectionCallbacks = true;
+ SelectEntry(BoundBy(selection, 0, GetLength(columnEntries[column]) - 1), column);
+
+ if(settings.InstantDescription.Enable)
+ {
+ ShowInstantDescription(columnEntries[currentColumn][selection]);
+ }
}
- CloseMenu(settings[DT_Menu_Settings_Object]);
- Create(settings, createEntries);
- SelectEntry(BoundBy(selection, 0, entryCount - 1));
+ noSelectionCallbacks = oldNoSelectionCallbacks;
+ }
+}
- if(disabledCallbacks)
+func ShowInstantDescription(map entry)
+{
+ if(entry.Description)
+ {
+ var icon = entry.InstantDescriptionIcon;
+ var iconString;
+ if(GetType(icon[1]) == C4V_String)
+ {
+ iconString = Format("Portrait:%i::%x::%s", icon[0], icon[2], icon[1]);
+ }
+ else
{
- noSelectionCallbacks = false;
+ iconString = Format("%i:%d", icon[0] || DT_Menu_IconDummy, icon[1]);
}
+ var pos = settings.InstantDescription.Position;
+ CustomMessage("@" .. entry.Description, 0, GetOwner(settings.Object), pos[0], pos[1], 0, settings.InstantDescription.Decoration, iconString, settings.InstantDescription.Flags);
+ }
+ else
+ {
+ HideInstantDescription();
}
}
-// ----------------------------------------------------------------------------
+func HideInstantDescription()
+{
+ CustomMessage("", 0, GetOwner(settings.Object), 0, 0, 0, 0, Format("%i", DT_Menu_IconDummy), settings.InstantDescription.Flags);
+}
-global func Menu__Setting(array setting) { return [DT_Menu_Type_Setting, setting]; }
-
-global func Menu_Symbol(id symbol) { return Menu__Setting([DT_Menu_Settings_Symbol, symbol]); }
-global func Menu_Object(object obj) { return Menu__Setting([DT_Menu_Settings_Object, obj]); }
-global func Menu__Extra(int extra, int data) { return Menu_Combined([Menu__Setting([DT_Menu_Settings_Extra, extra]), Menu_ExtraData(data)]); }
-global func Menu_Size(int width, int height) { return Menu__Setting([DT_Menu_Settings_Size, [width, height]]); }
-global func Menu_ExtraData(int data) { return Menu__Setting([DT_Menu_Settings_ExtraData, data]); }
-global func Menu_Caption(string caption) { return Menu__Setting([DT_Menu_Settings_Caption, caption]); }
-global func Menu_RefreshInterval(int interval) { return Menu__Setting([DT_Menu_Settings_RefreshInterval, interval + !interval]); }
-global func Menu_Selection(int selection) { return Menu__Setting([DT_Menu_Settings_Selection, selection]); }
-global func Menu__Style(int style) { return Menu__Setting([DT_Menu_Settings_Style, style]); }
-global func Menu__KeepOpen(int mode) { return Menu__Setting([DT_Menu_Settings_KeepOpen, mode]); }
-
-global func Menu_Extra_None() { return Menu__Extra(C4MN_Extra_None); }
-global func Menu_Extra_Components() { return Menu__Extra(C4MN_Extra_Components); }
-global func Menu_Extra_Value() { return Menu__Extra(C4MN_Extra_Value); }
-global func Menu_Extra_MagicValue(int compare) { return Menu__Extra(C4MN_Extra_MagicValue, compare); }
-global func Menu_Extra_Info() { return Menu__Extra(C4MN_Extra_Info); }
-global func Menu_Extra_ComponentsMagic() { return Menu__Extra(C4MN_Extra_ComponentsMagic); }
-
-global func Menu_Style_Normal() { return Menu__Style(C4MN_Style_Normal); }
-global func Menu_Style_Context() { return Menu__Style(C4MN_Style_Context); }
-global func Menu_Style_Info() { return Menu__Style(C4MN_Style_Info); }
-global func Menu_Style_Dialog() { return Menu__Style(C4MN_Style_Dialog); }
-global func Menu_Style_EqualItemHeight() { return Menu__Style(C4MN_Style_EqualItemHeight); }
-
-global func Menu_DontKeepOpen() { return Menu__KeepOpen(DT_Menu_KeepOpen_Not); }
-global func Menu_KeepOpen() { return Menu__KeepOpen(DT_Menu_KeepOpen_Keep); }
-global func Menu_ForceKeepOpen() { return Menu__KeepOpen(DT_Menu_KeepOpen_Force); }
-global func Menu_Refresh() { return Menu__KeepOpen(DT_Menu_KeepOpen_Refresh); }
-global func Menu_RefreshContinuously(int interval) { return Menu_Combined([Menu__KeepOpen(DT_Menu_KeepOpen_RefreshContinuously), Menu_RefreshInterval(interval)]); }
-global func Menu_Permanent() { return Menu__KeepOpen(DT_Menu_KeepOpen_Permanent); }
-
-global func Menu_ConditionAllowSelection() { return Menu__Setting([DT_Menu_Settings_ConditionAllowSelection, true]);}
-global func Menu_ConditionDenySelection() { return Menu__Setting([DT_Menu_Settings_ConditionAllowSelection, false]);}
-
-global func Menu_Callbacks(array callbacks) { return Menu__Setting([DT_Menu_Settings_Callbacks, callbacks]); }
-global func Menu_Decoration(id decoration) { return Menu__Setting([DT_Menu_Settings_Decoration, decoration]); }
-global func Menu_RequireAction(string action, object target) { return Menu__Setting([DT_Menu_Settings_RequireAction, [action, target]]); }
-global func Menu_KeepParentOnClose(bool dontKeep) { return Menu__Setting([DT_Menu_Settings_KeepParentOnClose, !dontKeep]); }
-global func Menu_ConditionDisableMode(mode) { return Menu__Setting([DT_Menu_Settings_ConditionDisableMode, mode]);}
-global func Menu_Vars(array vars) { return Menu__Setting([DT_Menu_Settings_Vars, vars]); }
-global func Menu_Closable() { return Menu__Setting([DT_Menu_Settings_Closable, true]); }
-global func Menu_NotClosable() { return Menu__Setting([DT_Menu_Settings_Closable, false]); }
+// ----------------------------------------------------------------------------
global func Menu_Callback(array callback, int types, array argBinding)
{
- argBinding = argBinding || [Menu_CallbackArg_Action, Menu_CallbackArg_Symbol, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_NewSelection, Menu_CallbackArg_OldSelection, Menu_CallbackArg_NewValue, Menu_CallbackArg_OldValue, Menu_CallbackArg_FromSubmenu];
- return [callback, types || Menu_CallbackType_Defaults, argBinding];
+ types ??= Menu_CallbackType_Defaults;
+ if(!(MN7I->CheckCallback(callback))) { FatalError("Assertion failed: Menu_Callback invalid callback used; assertion code: MN7I->CheckCallback(callback)"); }
+ if(!((types & ~Menu_CallbackType_All) == 0)) { FatalError("Assertion failed: Menu_Callback: invalid callback type(s) used; assertion code: (types & ~Menu_CallbackType_All) == 0"); }
+ argBinding = argBinding || [Menu_CallbackArg_Action, Menu_CallbackArg_Icon, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_NewSelection, Menu_CallbackArg_OldSelection, Menu_CallbackArg_NewValue, Menu_CallbackArg_OldValue, Menu_CallbackArg_FromSubMenu];
+ if(!(MN7I->ValidateMenuCallbackArgBinding(argBinding))) { FatalError("Assertion failed: Menu_Callback: invalid argBinding used; assertion code: MN7I->ValidateMenuCallbackArgBinding(argBinding)"); }
+ return [callback, types, argBinding];
}
-global func Menu_Entry_Caption(string Caption) { return [DT_Menu_Entry_Caption, Caption]; }
-global func Menu_Entry_Callbacks(array Callbacks) { return [DT_Menu_Entry_Callbacks, Callbacks]; }
-global func Menu_Entry_Count(int Count) { return [DT_Menu_Entry_Count, Count]; }
-global func Menu_Entry_InfoCaption(string InfoCaption) { return [DT_Menu_Entry_InfoCaption, InfoCaption]; }
-global func Menu_Entry_Extra(int Extra) { return [DT_Menu_Entry_Extra, Extra]; }
-global func Menu_Entry_XPar1(XPar1) { return [DT_Menu_Entry_XPar1, XPar1]; }
-global func Menu_Entry_XPar2(XPar2) { return [DT_Menu_Entry_XPar2, XPar2]; }
-global func Menu_Entry_Args(Args) { return [DT_Menu_Entry_Args, Args]; }
-global func Menu_Entry_Placeholder(bool Placeholder) { return [DT_Menu_Entry_Placeholder, Placeholder]; }
-global func Menu_Entry_Symbol(symbol, extra, int color)
-{
- if(GetType(symbol) == C4V_Array)
- {
- return [DT_Menu_Entry_Symbol, symbol];
- }
- else
- {
- return [DT_Menu_Entry_Symbol, [symbol, extra, color]];
- }
-}
-global func Menu_Entry_Condition(callback, int allowDisabledSelection) { return [DT_Menu_Entry_Condition, [callback, allowDisabledSelection]]; }
global func Menu_Entry_VariableCondition(array scopedVar, compare, int disableMode, bool invert, int allowDisabledSelection)
{
- return Menu_Entry_Condition(BindCallback(MN7I->MenuObjectCallback("VariableCondition"), [Bind(scopedVar), Bind(compare), Bind(disableMode), Bind(invert)]), allowDisabledSelection);
+ if(!(MN7I->CheckScopedVar(scopedVar))) { FatalError("Assertion failed: Menu_Entry_VariableCondition: Invalid scopedVar used; assertion code: MN7I->CheckScopedVar(scopedVar)"); }
+ if(!(!disableMode || (GetType(disableMode) == C4V_Int && disableMode >= 0 && disableMode <= Menu_ConditionReact_Max) || (GetType(disableMode) == C4V_Array && disableMode[0] == Menu_ConditionReact_CustomFormat && GetType(disableMode[1]) == C4V_String))) { FatalError("Assertion failed: Menu_ConditionDisableMode: invalid disableMode; assertion code: !disableMode || (GetType(disableMode) == C4V_Int && disableMode >= 0 && disableMode <= Menu_ConditionReact_Max) || (GetType(disableMode) == C4V_Array && disableMode[0] == Menu_ConditionReact_CustomFormat && GetType(disableMode[1]) == C4V_String)"); }
+ if(!(!allowDisabledSelection || (GetType(allowDisabledSelection) == C4V_Int && allowDisabledSelection >= 0 && allowDisabledSelection <= Menu_Condition_Max))) { FatalError("Assertion failed: Menu_ConditionAllowSelection: invalid allowDisabledSelection value; assertion code: !allowDisabledSelection || (GetType(allowDisabledSelection) == C4V_Int && allowDisabledSelection >= 0 && allowDisabledSelection <= Menu_Condition_Max)"); }
+ return BindCallback(MN7I->MenuObjectCallback("VariableCondition"), [Bind(scopedVar), Bind(compare), Bind(disableMode), Bind(invert)]);
}
-global func VariableCondition(array scopedVar, compare, int disableMode, bool invert)
+func VariableCondition(array scopedVar, compare, int disableMode, bool invert)
{
var disable = ScopedVar(scopedVar) != compare;
if(invert)
@@ -755,126 +1199,163 @@ global func VariableCondition(array scopedVar, compare, int disableMode, bool in
return disableMode;
}
else
-
{
return Menu_ConditionReact_Show;
}
}
-global func Menu_Entry_Object(object obj)
-{
- return Menu_Combined([Menu_Entry_Symbol(obj), Menu_Entry_Caption(GetName(obj)), Menu_Entry_InfoCaption(GetDesc(obj))]);
-}
-global func Menu_Entry(array settings)
+/*
+Menu_Entry({
+ Text = <Text>,
+ Icon = <Icon>,
+ Count = <Count>,
+ Description = <Description, shown as a Tooltip or as InstantDescription>,
+ Placeholder = <May the placeholder entry be selected>: true | >false<,
+ InstantDescriptionIcon = [iconID, iconIndex] | [iconID, portraitName, color],
+ Callbacks = [<Menu_Callback()>...],
+ Args = <Args for Callbacks>,
+ Condition = {
+ Callback = <Callback>,
+ AllowDisabledSelection = >Menu_Condition_Default< | Menu_Condition_AllowSelection | Menu_Condition_DenySelection
+ },
+ Extra = <Same as extra of AddMenuItem> >Also used internally<,
+ XPar1 = <Same as XPar1 of AddMenuItem> >Also used internally<,
+ XPar2 = <Same as XPar2 of AddMenuItem> >Also used internally<,
+ SubMenuInitColumnData = >Internal<: For SubMenus as columns,
+ SubMenuColumnEntries = >Internal<: For SubMenus as columns,
+ Type = >Internal<: DT_Menu_Type_Entry | DT_Menu_Type_Factory | DT_Menu_Type_Columns
+}) */
+
+
+global func Menu_Entry(map entry)
{
- if(!settings)
+ if(!entry)
{
- settings = [Menu_Entry_Placeholder(false)];
+ entry = { Placeholder = false };
+ }
+ else
+ {
+ entry = Extend({
+ Text = "",
+ Placeholder = -1,
+ Extra = 0,
+ }, entry, true);
}
- var namedArgs = [];
- namedArgs[DT_Menu_Entry_Caption] = "";
- namedArgs[DT_Menu_Entry_Placeholder] = -1;
- MN7I->NamedArgs(settings, namedArgs);
- if(!namedArgs[DT_Menu_Entry_InfoCaption])
+ if(!entry.Description)
{
- namedArgs[DT_Menu_Entry_Extra] |= C4MN_Add_ForceNoDesc;
+ entry.Extra |= C4MN_Add_ForceNoDesc;
}
- return [DT_Menu_Type_Entry, namedArgs];
+ entry.Type = DT_Menu_Type_Entry;
+ if(entry.Icon && GetType(entry.Icon) != C4V_Array)
+ {
+ if(GetType(entry.Icon) == C4V_C4Object)
+ {
+ entry.Icon = [entry.Icon, true];
+ }
+ else
+ {
+ entry.Icon = [entry.Icon];
+ }
+ }
+ return entry;
}
-global func Menu_SubMenu(array entrySettings, array menuEntry_Settings)
+global func Menu_SubMenu(map submenuSettings, bool asColumn)
{
- 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_Callbacks] = [Menu_Callback(MN7I->MenuObjectCallback("SubmenuItemCallback"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])];
+ if(!(submenuSettings.Entry)) { FatalError("Assertion failed: Menu_SubMenu: Non-selectable placeholder as submenu entry doesn't make sense; assertion code: submenuSettings.Entry"); }
+ if(!(submenuSettings && GetLength(submenuSettings) > 0)) { FatalError("Assertion failed: Menu_SubMenu: Empty submenu doesn't make sense; assertion code: submenuSettings && GetLength(submenuSettings) > 0"); }
+ var ret = Menu_Entry(submenuSettings.Entry);
+ ret.Args = [ret.Callbacks, ret.Args, submenuSettings, asColumn];
+ ret.Callbacks = [Menu_Callback(MN7I->MenuObjectCallback("SubMenuItemCallback"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])];
+
+ if(asColumn)
+ {
+ ret.SubMenuInitColumnData = submenuSettings;
+ }
+
return ret;
}
global func Menu_Factory(array callbacks, args, array binding)
{
- return [DT_Menu_Type_Factory, [callbacks, args, binding || [Menu_CallbackArg_Args, Menu_CallbackArg_EntryNumber, Menu_CallbackArg_Menu]]];
+ if(!(callbacks)) { FatalError("Assertion failed: Menu_Factory: callbacks are mandatory; assertion code: callbacks"); }
+ return {
+ Type = DT_Menu_Type_Factory,
+ Callbacks = callbacks,
+ Args = args,
+ Binding = binding || [Menu_CallbackArg_Args, Menu_CallbackArg_EntryNumber, Menu_CallbackArg_Menu]
+ };
+}
+
+// Menu_Columns
+// ([
+// [ first column entries ],
+// [ second column entries ],
+// ...
+// ])
+global func Menu_Columns(array columns)
+{
+ for(var column in columns)
+ {
+ if(column.Settings && GetLength(column.Settings) > 0)
+ {
+ FatalError("DT_Menu::Menu_Columns: menu settings can not appear in Menu_Columns()");
+ }
+ }
+
+ return {
+ Type = DT_Menu_Type_Columns,
+ Columns = columns
+ };
}
global func Menu_Text(string text, bool allowSelection)
{
- return Menu_Entry([Menu_Entry_Caption(text), Menu_Entry_Placeholder(allowSelection)]);
+ return Menu_Entry({
+ Text = text,
+ Placeholder = allowSelection
+ });
}
global func Menu_Blank(bool allowSelection)
{
- return Menu_Entry([Menu_Entry_Placeholder(allowSelection)]);
+ return Menu_Entry({
+ Placeholder = allowSelection
+ });
}
-func DeclineAcceptBack(string caption, symbol, string callback, array settings)
+func DeclineAcceptBack(string text, icon, string callback, map settings)
{
- var ret = Menu_Entry([Menu_Entry_Caption(caption), Menu_Combined(settings || [])]);
- ret[1][DT_Menu_Entry_Args] = [ret[1][DT_Menu_Entry_Callbacks], ret[1][DT_Menu_Entry_Args]];
- ret[1][DT_Menu_Entry_Callbacks] = Menu_Entry_Callbacks([Menu_Callback(MN7I->MenuObjectCallback(callback), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_Args, Menu_CallbackArg_All])])[1];
- ExtraSymbol(ret[1][DT_Menu_Entry_Caption], ret[1][DT_Menu_Entry_Symbol], symbol);
+ var ret = Menu_Entry(Extend({
+ Text = text,
+ }, settings || {}, true));
+
+ ret.Args = [ret.Callbacks, ret.Args];
+ ret.Callbacks = [Menu_Callback(MN7I->MenuObjectCallback(callback), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_Args, Menu_CallbackArg_All])];
+ ExtraIcon(ret.Text, ret.Icon, icon);
return ret;
}
-global func Menu_Decline(array settings)
+global func Menu_Decline(map settings)
{
return MN7I->DeclineAcceptBack("$Decline$", [MN7I, 2], "DeclineAcceptCommand", settings);
}
-global func Menu_Accept(array settings)
+global func Menu_Accept(map settings)
{
return MN7I->DeclineAcceptBack("$Accept$", [MN7I, 1], "DeclineAcceptCommand", settings);
}
-global func Menu_Back(array settings)
+global func Menu_Back(map settings)
{
return MN7I->DeclineAcceptBack("$Back$", [MN7I, 5], "BackCommand", settings);
}
-global func Menu_Adaptor_Type(int Type) { return [DT_Menu_Adaptor_Type, Type]; }
-global func Menu_Adaptor_Variable(array Variable) { return [DT_Menu_Adaptor_Variable, Variable]; }
-global func Menu_Adaptor_Callbacks(array Callbacks) { return [DT_Menu_Adaptor_Callbacks, Callbacks]; }
-global func Menu_Adaptor_MessageBoardText(string MessageBoardText) { return [DT_Menu_Adaptor_MessageBoardText, MessageBoardText]; }
-global func Menu_Adaptor_WrapAround(bool WrapAround) { return [DT_Menu_Adaptor_WrapAround, WrapAround]; }
-global func Menu_Adaptor_EnumSubmenuSymbol(id EnumSubmenuSymbol) { return [DT_Menu_Adaptor_EnumSubmenuSymbol, EnumSubmenuSymbol]; }
-global func Menu_Adaptor_EnumAllowUnknown(bool EnumAllowUnknown) { return [DT_Menu_Adaptor_EnumAllowUnknown, EnumAllowUnknown]; }
-global func Menu_Adaptor_EnumInline(bool EnumInline) { return [DT_Menu_Adaptor_EnumInline, EnumInline]; }
-global func Menu_Adaptor_EnumSubmenu(int callbackType) { return [DT_Menu_Adaptor_EnumSubmenu, callbackType]; }
-global func Menu_Adaptor_Limits(min, max, args)
-{
- return [DT_Menu_Adaptor_Limits, [min, max, args]];
-}
-global func Menu_Adaptor_StepSize(int step, force) { return [DT_Menu_Adaptor_StepSize, [step, force]]; }
-global func Menu_Adaptor_Enum(array enumVals, array layout, bool valuesAsSeparateLists)
-{
- MN7I->AdaptorLayout(layout, enumVals, valuesAsSeparateLists);
- return Menu_Combined([Menu_Adaptor_Type(Menu_AdaptorType_Enum), [DT_Menu_Adaptor_LayoutVals, [enumVals, layout]]]);
-}
-global func Menu_Adaptor_BitField(array fieldVals, array layout, bool valuesAsSeparateLists, bool bitPositionAsValue)
-{
- MN7I->AdaptorLayout(layout, fieldVals, valuesAsSeparateLists);
- if(bitPositionAsValue)
- {
- var valuePos = layout[Menu_Layout__ValuePos] - 1;
- for(var i = 0; i < GetLength(fieldVals); ++i)
- {
- fieldVals[i][valuePos] = 1 << fieldVals[i][valuePos];
- }
- }
- return Menu_Combined([Menu_Adaptor_Type(Menu_AdaptorType_BitField), [DT_Menu_Adaptor_LayoutVals, [fieldVals, layout]]]);
-}
-global func Menu_Adaptor_NoEmptyString() { return [DT_Menu_Adaptor_NoEmptyString, true]; }
-global func Menu_Adaptor_EnumSubmenuCaption(string menuCaption, string entryCaption)
-{
- return [DT_Menu_Adaptor_EnumSubmenuCaption, [menuCaption, entryCaption]];
-}
-global func Menu_Adaptor_Boolean() { return Menu_Adaptor_Type(Menu_AdaptorType_Boolean); }
-global func Menu_Adaptor_Integer() { return Menu_Adaptor_Type(Menu_AdaptorType_Integer); }
-global func Menu_Adaptor_String() { return Menu_Adaptor_Type(Menu_AdaptorType_String); }
-global func Menu_Adaptor_ID() { return Menu_Adaptor_Type(Menu_AdaptorType_ID); }
-
func AdaptorLayout(array& layout, array& vals, bool valuesAsSeparateLists)
{
- layout = layout || [Menu_Layout_Value | Menu_Layout_Caption];
+ if(!(vals)) { FatalError("Assertion failed: AdaptorLayout: vals are mandatory; assertion code: vals"); }
+ layout = layout || [Menu_Layout_Value | Menu_Layout_Text];
var layoutMap = [];
var index = 1;
for(var val in layout)
@@ -886,9 +1367,9 @@ func AdaptorLayout(array& layout, array& vals, bool valuesAsSeparateLists)
layoutMap[noFlag] = index;
}
- if(val & Menu_Layout_Caption)
+ if(val & Menu_Layout_Text)
{
- layoutMap[Menu_Layout__CaptionPos] = index;
+ layoutMap[Menu_Layout__TextPos] = index;
}
if(val & Menu_Layout_Value)
@@ -903,6 +1384,7 @@ func AdaptorLayout(array& layout, array& vals, bool valuesAsSeparateLists)
++index;
}
+ if(!(layoutMap[Menu_Layout__ValuePos])) { FatalError("Assertion failed: AdaptorLayout: Menu_Layout_Value is mandatory; assertion code: layoutMap[Menu_Layout__ValuePos]"); }
layout = layoutMap;
@@ -923,28 +1405,120 @@ func AdaptorLayout(array& layout, array& vals, bool valuesAsSeparateLists)
}
}
-global func Menu_Adaptor(array entrySettings, array adaptorSettings)
+/*
+Menu_Adaptor({
+ Entry = {
+ sames as for Menu_Entry
+ },
+ Adaptor = {
+ Type = Menu_AdaptorType_Boolean | Menu_AdaptorType_Integer | Menu_AdaptorType_String | Menu_AdaptorType_ID | Menu_AdaptorType_Enum | Menu_AdaptorType_BitField,
+ Variable = <ScopedVar> storing the value,
+ Callbacks = [<Menu_Callback()>...],
+ Args = <Args for Callbacks>,
+ MessageBoardText = <Text shown in front of the MessageBoard>,
+ Limits = [<Min>, <Max>] for Integer-Type,
+ StepSize = [<StepSize for increasing and decreasing>, <StepSize for MessageBoard input>] for Integer-Type,
+ NoEmptyString = <Disallow setting the value to an empty string for String-Type>: true | >false<,
+ EntryIndex = >Internal<: used for MessageBoard callbacks,
+ WrapAround = <Wrap around the allowed values at their limits for Integer- and Enum-Type>: true <else for Enum> | false <else for Integer>,
+ Enum = <only for Enum-Type>: {
+ Values = [<[Values according to Layout]>...],
+ Layout = [<[Menu_Layout_*, some can be combined with &]>...],
+ ValuesAsSeparateLists = <Values is a "struct of arrays" instead of "array of structs">: true | >false<,
+ Inline = <Always show all values inlined in the containing menu>: true | >false<,
+ AllowUnknown = <Are unknown values allowed for the specified var? If not, it will be overwritten.>: true | >false<,
+ SubMenu = <Settings specific to the optional submenu possibility>: {
+ On = <CallbackType when to show>: >Menu_CallbackType_Special2< | true to show as extra column | Menu_CallbackType_None <to disable> | Menu_CallbackType_*,
+ Icon = <Icon to use for the SubMenu-Header>,
+ Title = <Title to use for the SubMenu-Header>,
+ Text = <Text to use for the individual Entries>,
+ }
+ },
+ BitField = <only for BitField-Type>: {
+ Values = [<[Values according to Layout]>...],
+ Layout = [<[Menu_Layout_*, some can be combined with &]>...],
+ ValuesAsSeparateLists = <Values is a "struct of arrays" instead of "array of structs">: true | >false<,
+ BitPositionAsValue = <Instead of the values being a bit-mask, the values denote the bit-position>: true | >false<,
+ Mask = >Internal<
+ }
+ }
+}) */
+
+global func Menu_Adaptor(map settings)
{
- var adaptorArgs = [];
+ if(!(settings.Adaptor && GetLength(settings.Adaptor) > 0)) { FatalError("Assertion failed: Menu_Adaptor: Adaptor without settings doesn't make sense; assertion code: settings.Adaptor && GetLength(settings.Adaptor) > 0"); }
+ if(!(settings.Entry && GetLength(settings.Entry) > 0)) { FatalError("Assertion failed: Menu_Adaptor: Non-selectable placeholder as adaptor entry doesn't make sense; assertion code: settings.Entry && GetLength(settings.Entry) > 0"); }
- adaptorArgs[DT_Menu_Adaptor_WrapAround] = -1;
- adaptorArgs[DT_Menu_Adaptor_EnumSubmenu] = Menu_CallbackType_Special2;
+ if(!settings.Adaptor.Type)
+ {
+ if(settings.Adaptor.Enum)
+ {
+ settings.Adaptor.Type = Menu_AdaptorType_Enum;
+ }
+ else if(settings.Adaptor.BitField)
+ {
+ settings.Adaptor.Type = Menu_AdaptorType_BitField;
+ }
+ else
+ {
+ FatalError("Menu_Adaptor: Adaptor.Type is missing!");
+ }
+ }
- MN7I->NamedArgs(adaptorSettings, adaptorArgs);
+ settings = Extend({
+ Adaptor = {
+ WrapAround = -1,
+ Enum = {
+ SubMenu = {
+ On = Menu_CallbackType_Special2,
+ }
+ }
+ }
+ }, settings, true);
- if(adaptorArgs[DT_Menu_Adaptor_WrapAround] == -1)
+ if(settings.Adaptor.WrapAround == -1)
{
- if(adaptorArgs[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Integer)
+ if(settings.Adaptor.Type == Menu_AdaptorType_Integer)
{
- adaptorArgs[DT_Menu_Adaptor_WrapAround] = false;
+ settings.Adaptor.WrapAround = false;
}
- else if(adaptorArgs[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Enum)
+ else if(settings.Adaptor.Type == Menu_AdaptorType_Enum)
{
- adaptorArgs[DT_Menu_Adaptor_WrapAround] = true;
+ settings.Adaptor.WrapAround = true;
}
}
- return Menu_Factory([MN7I->MenuObjectCallback("AdaptorFactory")], [Menu_Entry(entrySettings)[1], adaptorArgs, entrySettings]);
+ if(settings.Adaptor.Type == Menu_AdaptorType_BitField)
+ {
+ var layout = settings.Adaptor.BitField.Layout;
+ var fieldVals = settings.Adaptor.BitField.Values;
+ MN7I->AdaptorLayout(layout, fieldVals, settings.Adaptor.BitField.ValuesAsSeparateLists);
+ if(settings.Adaptor.BitField.BitPositionAsValue)
+ {
+ var valuePos = layout[Menu_Layout__ValuePos] - 1;
+ for(var i = 0; i < GetLength(fieldVals); ++i)
+ {
+ if(!(!fieldVals[i][valuePos] || GetType(fieldVals[i][valuePos]) == C4V_Int)) { FatalError(Format("Assertion failed: Menu_Adaptor_BitField: Bit value must be an integer; got: %v; assertion code: !fieldVals[i][valuePos] || GetType(fieldVals[i][valuePos]) == C4V_Int", fieldVals[i][valuePos])); }
+ if(!(fieldVals[i][valuePos] >= 0 && fieldVals[i][valuePos] < 32)) { FatalError("Assertion failed: Menu_Adaptor_BitField: Bit value out of range; assertion code: fieldVals[i][valuePos] >= 0 && fieldVals[i][valuePos] < 32"); }
+ fieldVals[i][valuePos] = 1 << fieldVals[i][valuePos];
+ }
+ }
+ settings.Adaptor.BitField.Values = fieldVals;
+ settings.Adaptor.BitField.Layout = layout;
+
+ var valuePos = layout[Menu_Layout__ValuePos] - 1;
+ for(var fieldPart in fieldVals)
+ {
+ if(!(GetType(fieldPart[valuePos]) == C4V_Int)) { FatalError(Format("Assertion failed: Menu_Adaptor_BitField: Flag mask (Menu_Layout_Value) must be a non-zero integer; got %v; assertion code: GetType(fieldPart[valuePos]) == C4V_Int", fieldPart[valuePos])); }
+ }
+
+ }
+ else if(settings.Adaptor.Type == Menu_AdaptorType_Enum)
+ {
+ MN7I->AdaptorLayout(settings.Adaptor.Enum.Layout, settings.Adaptor.Enum.Values, settings.Adaptor.Enum.ValuesAsSeparateLists);
+ }
+
+ return Menu_Factory([MN7I->MenuObjectCallback("AdaptorFactory")], [Menu_Entry(settings.Entry), settings], [Menu_CallbackArg_Args, Menu_CallbackArg_EntryNumber, Menu_CallbackArg_EntryColumn]);
}
func EnumValPos(array enumVals, array layout, val, bool allowUnknown)
@@ -956,64 +1530,67 @@ func EnumValPos(array enumVals, array layout, val, bool allowUnknown)
return i;
}
}
+ if(!allowUnknown)
+ {
+ Log("WARNING: DT_Menu::EnumValPos: Current value %v is not in enum: %v", val, enumVals);
+ }
return -1;
}
-func BooleanToggleCaption(bool val, string& caption, &symbol)
+func BooleanToggleText(bool val, string& text, &icon)
{
val = !!val;
- ExtraSymbol(caption, symbol, [MN7I, 2 - val, !val && RGB(128, 128, 128)]);
+ ExtraIcon(text, icon, [MN7I, 2 - val, !val && RGB(128, 128, 128)]);
}
-func InlineSymbol(string& caption, symbol)
+func InlineIcon(string& text, icon)
{
- if(symbol[2])
+ if(icon[2])
{
- caption = Format("<c %x>{{%i:%d}}</c> %s", symbol[2], symbol[0], symbol[1], caption);
+ text = Format("<c %x>{{%i:%d}}</c> %s", icon[2], icon[0], icon[1], text);
}
else
{
- caption = Format("{{%i:%d}} %s", symbol[0], symbol[1], caption);
+ text = Format("{{%i:%d}} %s", icon[0], icon[1], text);
}
}
-func ExtraSymbol(string& caption, &symbol, extraSymbol)
+func ExtraIcon(string& text, &icon, extraIcon)
{
- if(GetType(extraSymbol) == C4V_C4ID)
+ if(GetType(extraIcon) == C4V_C4ID)
{
- extraSymbol = [extraSymbol];
+ extraIcon = [extraIcon];
}
- if(symbol && extraSymbol && GetType(extraSymbol[0]) == C4V_C4ID)
+ if(icon && extraIcon && GetType(extraIcon[0]) == C4V_C4ID)
{
- InlineSymbol(caption, extraSymbol);
+ InlineIcon(text, extraIcon);
}
else
{
- symbol = extraSymbol;
+ icon = extraIcon;
}
}
-func EnumEntrySettings(string& caption, &symbol, string& infoCaption, int index, array args, array entry)
+func EnumEntrySettings(string& text, &icon, string& description, int index, map args, map entry)
{
- var layoutVals = args[DT_Menu_Adaptor_LayoutVals];
- var layout = layoutVals[1];
- layoutVals = layoutVals[0];
+ var layoutVals = args.Enum.Values;
+ var layout = args.Enum.Layout;
- caption = entry[DT_Menu_Entry_Caption];
- if(layout[Menu_Layout__CaptionPos])
+ text = entry.Text;
+ if(layout[Menu_Layout__TextPos])
{
- caption = Format(caption, layoutVals[index][layout[Menu_Layout__CaptionPos] - 1]);
+ text = Format(text, layoutVals[index][layout[Menu_Layout__TextPos] - 1]);
}
- if(layout[Menu_Layout_Symbol])
+ if(layout[Menu_Layout_Icon])
{
- ExtraSymbol(caption, symbol, layoutVals[index][layout[Menu_Layout_Symbol] - 1]);
+ ExtraIcon(text, icon, layoutVals[index][layout[Menu_Layout_Icon] - 1]);
}
- if(layout[Menu_Layout_InfoCaption])
+ if(layout[Menu_Layout_Description])
{
- infoCaption = Format(infoCaption, layoutVals[index][layout[Menu_Layout_InfoCaption] - 1]);
+ description = Format(description, layoutVals[index][layout[Menu_Layout_Description] - 1]);
}
}
@@ -1034,42 +1611,47 @@ func AdaptorGetLimits(array limits)
else
{
ret[i] = Call(limits[i], limits[3]);
+ if(!(!ret[i] || GetType(ret[i]) == C4V_Int)) { FatalError(Format("Assertion failed: Menu_Adaptor_Limits: Got invalid callback value for %s: %v; assertion code: !ret[i] || GetType(ret[i]) == C4V_Int", ["min", "max"][i], ret[i])); }
}
}
return ret;
}
-func AdaptorFactory(args, int entryIndex)
+func AdaptorFactory(args, int entryIndex, int entryColumn)
{
var origArgs = args;
var entry = args[0];
- var entrySettings = args[2];
- args = args[1];
- var caption = entry[DT_Menu_Entry_Caption];
- var infoCaption = entry[DT_Menu_Entry_InfoCaption];
- var symbol = entry[DT_Menu_Entry_Symbol];
- var val = ScopedVar(args[DT_Menu_Adaptor_Variable]);
+ var entrySettings = args[1].Entry;
+ args = args[1].Adaptor;
+ var text = entry.Text;
+ var description = entry.Description;
+ var icon = entry.Icon;
+ var val = ScopedVar(args.Variable);
var defaultMsgboardText = "$EnterValue$";
- var retSubmenu;
+ var retSubMenu;
- args[DT_Menu_Adaptor_Args] = entry[DT_Menu_Entry_Args];
- args[DT_Menu_Adaptor_EntryIndex] = entryIndex;
- args[DT_Menu_Adaptor_Callbacks] = args[DT_Menu_Adaptor_Callbacks] || [];
- if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Boolean)
+ args.Args = entry.Args;
+ args.EntryIndex = [entryIndex, entryColumn];
+ args.Callbacks = args.Callbacks || [];
+ if(args.Type == Menu_AdaptorType_Boolean)
{
- BooleanToggleCaption(val, caption, symbol);
+ if(!(GetType(val) == C4V_Any || GetType(val) == C4V_Bool)) { FatalError("Assertion failed: AdaptorFactory: Value of Menu_Adaptor_Variable has wrong type; assertion code: GetType(val) == C4V_Any || GetType(val) == C4V_Bool"); }
+ BooleanToggleText(val, text, icon);
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_String)
+ else if(args.Type == Menu_AdaptorType_String)
{
- caption = Format(entry[DT_Menu_Entry_Caption], val);
+ if(!(GetType(val) == C4V_Any || GetType(val) == C4V_String)) { FatalError("Assertion failed: AdaptorFactory: Value of Menu_Adaptor_Variable has wrong type; assertion code: GetType(val) == C4V_Any || GetType(val) == C4V_String"); }
+ text = Format(entry.Text, val);
defaultMsgboardText = "$EnterText$";
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Integer)
+ else if(args.Type == Menu_AdaptorType_Integer)
{
- var limits = AdaptorGetLimits(args[DT_Menu_Adaptor_Limits]), max, min;
- var wrapAround = args[DT_Menu_Adaptor_WrapAround];
+ if(!(GetType(val) == C4V_Any || GetType(val) == C4V_Int)) { FatalError("Assertion failed: AdaptorFactory: Value of Menu_Adaptor_Variable has wrong type; assertion code: GetType(val) == C4V_Any || GetType(val) == C4V_Int"); }
+ val ??= 0;
+ var limits = AdaptorGetLimits(args.Limits), max, min;
+ var wrapAround = args.WrapAround;
if(limits && !wrapAround)
{
if(val >= limits[1])
@@ -1081,162 +1663,172 @@ func AdaptorFactory(args, int entryIndex)
min = true;
}
}
- caption = Format("%s %s", Format(entry[DT_Menu_Entry_Caption], val), ["{{MN7I:4}}", "<c 808080>{{MN7I:4}}</c>"][min]);
- ExtraSymbol(caption, symbol, [MN7I, 3, max && RGB(128, 128, 128)]);
+ text = Format(entry.Text, val) .. " " .. ["{{MN7I:4}}", "<c 808080>{{MN7I:4}}</c>"][min];
+ ExtraIcon(text, icon, [MN7I, 3, max && RGB(128, 128, 128)]);
defaultMsgboardText = "$EnterNumber$";
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_ID)
+ else if(args.Type == Menu_AdaptorType_ID)
{
- caption = Format(entry[DT_Menu_Entry_Caption], val && GetName(0, val) || "");
+ if(!(GetType(val) == C4V_Any || GetType(val) == C4V_C4ID)) { FatalError("Assertion failed: AdaptorFactory: Value of Menu_Adaptor_Variable has wrong type; assertion code: GetType(val) == C4V_Any || GetType(val) == C4V_C4ID"); }
+ text = Format(entry.Text, val && GetName(0, val) || "");
if(val)
{
- ExtraSymbol(caption, symbol, val);
+ ExtraIcon(text, icon, val);
}
defaultMsgboardText = "$EnterIDOrName$";
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Enum)
+ else if(args.Type == Menu_AdaptorType_Enum)
{
- if(args[DT_Menu_Adaptor_EnumInline])
+ if(args.Enum.Inline)
{
- return AdaptorEnumSubmenuFactory([args, entry]);
+ return AdaptorEnumSubMenuFactory([args, entry]);
}
- var layoutVals = args[DT_Menu_Adaptor_LayoutVals];
- var layout = layoutVals[1];
- layoutVals = layoutVals[0];
+ var layoutVals = args.Enum.Values;
+ var layout = args.Enum.Layout;
- var index = EnumValPos(layoutVals, layout, val, args[DT_Menu_Adaptor_EnumAllowUnknown]);
+ var index = EnumValPos(layoutVals, layout, val, args.Enum.AllowUnknown);
if(index == -1)
{
- if(!args[DT_Menu_Adaptor_EnumAllowUnknown])
+ if(!args.Enum.AllowUnknown)
{
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = layoutVals[0][layout[Menu_Layout__ValuePos] - 1];
+ ScopedVar(args.Variable) = layoutVals[0][layout[Menu_Layout__ValuePos] - 1];
}
index = 0;
}
- var submenuSymbol = symbol;
+ var submenuIcon = icon;
- EnumEntrySettings(caption, symbol, infoCaption, index, args, entry);
+ EnumEntrySettings(text, icon, description, index, args, entry);
- if(args[DT_Menu_Adaptor_EnumSubmenu] != Menu_CallbackType_None)
+ if(args.Enum.SubMenu.On != Menu_CallbackType_None)
{
- args[DT_Menu_Adaptor_MessageBoardText] = args[DT_Menu_Adaptor_MessageBoardText] || defaultMsgboardText;
+ args.MessageBoardText = args.MessageBoardText || defaultMsgboardText;
- retSubmenu = [Menu_Factory([MenuObjectCallback("AdaptorEnumSubmenuFactory")], [args, entry])];
+ retSubMenu = [Menu_Factory([MenuObjectCallback("AdaptorEnumSubMenuFactory")], origArgs[1])];
}
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_BitField)
+ else if(args.Type == Menu_AdaptorType_BitField)
{
return AdaptorBitFieldItemsFactory(origArgs);
}
- args[DT_Menu_Adaptor_MessageBoardText] = args[DT_Menu_Adaptor_MessageBoardText] || defaultMsgboardText;
- if(!retSubmenu)
+ args.MessageBoardText = args.MessageBoardText || defaultMsgboardText;
+ if(!retSubMenu)
{
- return [Menu_Entry([Menu_Combined(entrySettings), Menu_Entry_Caption(caption), Menu_Entry_Callbacks([Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])]), Menu_Entry_Symbol(symbol), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Args(args)])];
+ return [Menu_Entry(Extend(entrySettings, { Text = text, Callbacks = [Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])], Icon = icon, Description = description, Args = args }, true))];
}
else
{
- return [Menu_SubMenu([Menu_Combined(entrySettings), Menu_Entry_Caption(caption), Menu_Entry_Callbacks([Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])]), Menu_Entry_Symbol(symbol), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Args(args)], retSubmenu)];
+ return [Menu_SubMenu({ Entry = Extend(entrySettings, { Text = text, Callbacks = [Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])], Icon = icon, Description = description, Args = args }, true), Entries = retSubMenu }, GetType(args.Enum.SubMenu.On) == C4V_Bool && args.Enum.SubMenu.On)];
}
}
-func AdaptorEnumSubmenuFactory(array args)
+func AdaptorEnumSubMenuFactory(map args)
{
- var entry = args[1];
- args = args[0];
- var layoutVals = args[DT_Menu_Adaptor_LayoutVals];
- var layout = layoutVals[1];
- layoutVals = layoutVals[0];
+ var entry = args.Entry;
+ args = args.Adaptor;
+ var layoutVals = args.Enum.Values;
+ var layout = args.Enum.Layout;
- var submenuCaption = args[DT_Menu_Adaptor_EnumSubmenuCaption];
- var submenuSymbol = [];
+ var submenuIcon = [];
- if(submenuCaption && submenuCaption[0])
+ if(args.Enum.SubMenu.Title)
{
- entry[DT_Menu_Entry_Caption] = submenuCaption[0];
+ entry.Text = args.Enum.SubMenu.Title;
}
- var index = EnumValPos(layoutVals, layout, ScopedVar(args[DT_Menu_Adaptor_Variable]), args[DT_Menu_Adaptor_EnumAllowUnknown]);
- var symbol, infoCaption;
- EnumEntrySettings(submenuCaption, symbol, infoCaption, index, args, entry);
+ var index = EnumValPos(layoutVals, layout, ScopedVar(args.Variable), args.Enum.AllowUnknown);
+ var title, icon, description;
+ EnumEntrySettings(title, icon, description, index, args, entry);
var ret;
- if(args[DT_Menu_Adaptor_EnumInline])
+ if(args.Enum.Inline)
{
- ret = [];
+ ret = {
+ Entries = []
+ };
}
else
{
- ret = [
- Menu_Selection(index),
- Menu_KeepParentOnClose(),
- Menu_DontKeepOpen(),
- Menu_Caption(submenuCaption)
- ];
+ ret = {
+ Settings = {
+ Selection = [index, 0],
+ KeepParentOnClose = true,
+ KeepOpen = Menu_KeepOpen_Not,
+ Title = title
+ },
+ Entries = []
+ };
}
- if(args[DT_Menu_Adaptor_EnumSubmenuSymbol])
+ if(args.Enum.SubMenu.Icon)
{
- submenuSymbol = [args[DT_Menu_Adaptor_EnumSubmenuSymbol]];
+ submenuIcon = [args.Enum.SubMenu.Icon];
}
- else if(!submenuSymbol && symbol && GetType(symbol[0]) == C4V_C4ID)
+ else if(icon && GetType(icon[0]) == C4V_C4ID)
{
- submenuSymbol = symbol;
+ submenuIcon = icon;
}
- if(submenuSymbol && GetType(submenuSymbol[0]) == C4V_C4ID)
+ if(submenuIcon && GetType(submenuIcon[0]) == C4V_C4ID)
{
- ArrayAppend(ret, Menu_Symbol(submenuSymbol[0]));
+ ret.Settings.Icon = submenuIcon[0];
}
- var caption;
-
- var submenuCaption = args[DT_Menu_Adaptor_EnumSubmenuCaption];
- if(submenuCaption && submenuCaption[1])
+ var text;
+ if(args.Enum.SubMenu.Text)
{
- entry[DT_Menu_Entry_Caption] = submenuCaption[1];
+ entry.Text = args.Enum.SubMenu.Text;
}
for(var i = 0; i < GetLength(layoutVals); ++i)
{
- symbol = 0;
- EnumEntrySettings(caption, symbol, infoCaption, i, args, entry);
- if(args[DT_Menu_Adaptor_EnumInline])
+ icon = 0;
+ EnumEntrySettings(text, icon, description, i, args, entry);
+ if(args.Enum.Inline)
{
- BooleanToggleCaption(i == index, caption, symbol);
+ BooleanToggleText(i == index, text, icon);
}
- ArrayAppend(ret, Menu_Entry([Menu_Entry_Caption(caption), Menu_Entry_Symbol(symbol), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Callbacks([Menu_Callback(MenuObjectCallback("AdaptorEnumSubmenuItem"), Menu_CallbackType_Defaults, [Menu_CallbackArg_Args, Menu_CallbackArg_All])]), Menu_Entry_Args([i, args])]));
+ else if(i == index)
+ {
+ ExtraIcon(text, icon, [MN7I, 1]);
+ }
+ ret.Entries[] = Menu_Entry({
+ Text = text,
+ Icon = icon,
+ Description = description,
+ Callbacks = [Menu_Callback(MenuObjectCallback("AdaptorEnumSubMenuItem"), Menu_CallbackType_Defaults, [Menu_CallbackArg_Args, Menu_CallbackArg_All])],
+ Args = [i, args]
+ });
}
return ret;
}
-func AdaptorEnumSubmenuItem(args, array allArgs)
+func AdaptorEnumSubMenuItem(args, array allArgs)
{
var index = args[0];
args = args[1];
- var val = ScopedVar(args[DT_Menu_Adaptor_Variable]);
+ var val = ScopedVar(args.Variable);
var oldVal = val;
- var enumVals = args[DT_Menu_Adaptor_LayoutVals];
- var layout = enumVals[1];
- enumVals = enumVals[0];
+ var enumVals = args.Enum.Values;
+ var layout = args.Enum.Layout;
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = val = enumVals[index][layout[Menu_Layout__ValuePos] - 1];
+ ScopedVar(args.Variable) = val = enumVals[index][layout[Menu_Layout__ValuePos] - 1];
- allArgs[Menu_CallbackArg_Args] = args[DT_Menu_Adaptor_Args];
- allArgs[Menu_CallbackArg_FromSubmenu] = true;
+ allArgs[Menu_CallbackArg_Args] = args.Args;
+ allArgs[Menu_CallbackArg_FromSubMenu] = true;
- var reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs);
+ var reaction = AdaptorCommandCallChangedCallback(args.Callbacks, val, oldVal, allArgs);
if(reaction != Menu_React_None)
{
return reaction;
}
- if(args[DT_Menu_Adaptor_EnumInline])
+ if(args.Enum.Inline)
{
return Menu_React_Refresh;
}
@@ -1247,47 +1839,47 @@ func AdaptorEnumSubmenuItem(args, array allArgs)
func AdaptorBitFieldItemsFactory(args)
{
var entry = args[0];
- var entrySettings = args[2];
- args = args[1];
+ var entrySettings = args[1].Entry;
+ args = args[1].Adaptor;
- var caption = entry[DT_Menu_Entry_Caption];
- var infoCaption = entry[DT_Menu_Entry_InfoCaption];
- var symbol = entry[DT_Menu_Entry_Symbol];
- var fieldValue = ScopedVar(args[DT_Menu_Adaptor_Variable]);
+ var text = entry.Text;
+ var description = entry.Description;
+ var icon = entry.Icon;
+ var fieldValue = ScopedVar(args.Variable);
+ if(!(GetType(fieldValue) == C4V_Any || GetType(fieldValue) == C4V_Int)) { FatalError("Assertion failed: AdaptorFactory: Value of Menu_Adaptor_Variable has wrong type; assertion code: GetType(fieldValue) == C4V_Any || GetType(fieldValue) == C4V_Int"); }
- var layoutVals = args[DT_Menu_Adaptor_LayoutVals];
- var layout = layoutVals[1];
- layoutVals = layoutVals[0];
+ var layoutVals = args.BitField.Values;
+ var layout = args.BitField.Layout;
var ret = [];
for(var val in layoutVals)
{
var mask = val[layout[Menu_Layout__ValuePos] - 1];
- caption = entry[DT_Menu_Entry_Caption] || "%s";
- if(layout[Menu_Layout__CaptionPos])
+ text = entry.Text || "%s";
+ if(layout[Menu_Layout__TextPos])
{
- caption = Format(caption, val[layout[Menu_Layout__CaptionPos] - 1]);
+ text = Format(text, val[layout[Menu_Layout__TextPos] - 1]);
}
- symbol = entry[DT_Menu_Entry_Symbol];
- BooleanToggleCaption((fieldValue & mask) == mask, caption, symbol);
- if(layout[Menu_Layout_Symbol])
+ icon = entry.Icon;
+ BooleanToggleText((fieldValue & mask) == mask, text, icon);
+ if(layout[Menu_Layout_Icon])
{
- ExtraSymbol(caption, symbol, val[layout[Menu_Layout_Symbol] - 1]);
+ ExtraIcon(text, icon, val[layout[Menu_Layout_Icon] - 1]);
}
- if(layout[Menu_Layout_InfoCaption])
+ if(layout[Menu_Layout_Description])
{
- infoCaption = Format(infoCaption, val[layout[Menu_Layout_InfoCaption] - 1]);
+ description = Format(description, val[layout[Menu_Layout_Description] - 1]);
}
- args[DT_Menu_Adaptor_Mask] = mask;
+ args.BitField.Mask = mask;
- ArrayAppend(ret, Menu_Entry([Menu_Combined(entrySettings), Menu_Entry_Caption(caption), Menu_Entry_Callbacks([Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])]), Menu_Entry_Symbol(symbol), Menu_Entry_InfoCaption(infoCaption), Menu_Entry_Args(args)]));
+ ret[] = Menu_Entry(Extend(entrySettings, { Text = text, Callbacks = [Menu_Callback(MenuObjectCallback("AdaptorCommand"), Menu_CallbackType_All, [Menu_CallbackArg_Action, Menu_CallbackArg_MenuObject, Menu_CallbackArg_Args, Menu_CallbackArg_All])], Icon = icon, Description = description, Args = args }, true));
}
- return ret;
+ return { Entries = ret };
}
func WrapOrBind(int val, array limits, bool wrap)
@@ -1339,24 +1931,24 @@ func AdaptorCommandCallChangedCallback(callbacks, val, oldVal, array allArgs)
func AdaptorCommand(int action, object obj, args, array allArgs)
{
- var val = ScopedVar(args[DT_Menu_Adaptor_Variable]);
+ var val = ScopedVar(args.Variable);
var oldVal = val;
- allArgs[Menu_CallbackArg_Args] = args[DT_Menu_Adaptor_Args];
+ allArgs[Menu_CallbackArg_Args] = args.Args;
- var reaction = CallCallbacks(args[DT_Menu_Adaptor_Callbacks], action, allArgs, Menu_React_Refresh, true);
+ var reaction = CallCallbacks(args.Callbacks, action, allArgs, Menu_React_Refresh, true);
if(reaction != Menu_React_Refresh)
{
return reaction;
}
- if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Boolean)
+ if(args.Type == Menu_AdaptorType_Boolean)
{
if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2))
{
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = val = !val;
- reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs);
+ ScopedVar(args.Variable) = val = !val;
+ reaction = AdaptorCommandCallChangedCallback(args.Callbacks, val, oldVal, allArgs);
if(reaction != Menu_React_None)
{
return reaction;
@@ -1367,11 +1959,11 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
return Menu_React_KeepOpen;
}
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Integer)
+ else if(args.Type == Menu_AdaptorType_Integer)
{
- var limits = AdaptorGetLimits(args[DT_Menu_Adaptor_Limits]);
- var wrapAround = args[DT_Menu_Adaptor_WrapAround];
- var step = args[DT_Menu_Adaptor_StepSize];
+ var limits = AdaptorGetLimits(args.Limits);
+ var wrapAround = args.WrapAround;
+ var step = args.StepSize;
var stepSize = 1;
if(step)
{
@@ -1382,8 +1974,8 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
if(msgBoardMode == 0)
{
msgBoardMode = Menu_AdaptorType_Integer + 1;
- msgBoardEntry = args[DT_Menu_Adaptor_EntryIndex];
- CallMessageBoard(this, false, args[DT_Menu_Adaptor_MessageBoardText], GetOwner(obj));
+ msgBoardEntry = args.EntryIndex;
+ CallMessageBoard(this, false, args.MessageBoardText, GetOwner(obj));
}
return Menu_React_KeepOpen;
}
@@ -1404,8 +1996,8 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
if(val != oldVal)
{
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = val;
- reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs);
+ ScopedVar(args.Variable) = val;
+ reaction = AdaptorCommandCallChangedCallback(args.Callbacks, val, oldVal, allArgs);
if(reaction != Menu_React_None)
{
return reaction;
@@ -1416,15 +2008,15 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
return Menu_React_KeepOpen;
}
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_String)
+ else if(args.Type == Menu_AdaptorType_String)
{
if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2))
{
if(msgBoardMode == 0)
{
msgBoardMode = Menu_AdaptorType_String + 1;
- msgBoardEntry = args[DT_Menu_Adaptor_EntryIndex];
- CallMessageBoard(this, false, args[DT_Menu_Adaptor_MessageBoardText], GetOwner(obj));
+ msgBoardEntry = args.EntryIndex;
+ CallMessageBoard(this, false, args.MessageBoardText, GetOwner(obj));
}
}
if(action == Menu_CallbackType_Close)
@@ -1436,44 +2028,42 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
return Menu_React_KeepOpen;
}
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_ID)
+ else if(args.Type == Menu_AdaptorType_ID)
{
if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2))
{
if(msgBoardMode == 0)
{
msgBoardMode = Menu_AdaptorType_ID + 1;
- msgBoardEntry = args[DT_Menu_Adaptor_EntryIndex];
- CallMessageBoard(this, false, args[DT_Menu_Adaptor_MessageBoardText], GetOwner(obj));
+ msgBoardEntry = args.EntryIndex;
+ CallMessageBoard(this, false, args.MessageBoardText, GetOwner(obj));
}
}
return Menu_React_KeepOpen;
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_Enum)
+ else if(args.Type == Menu_AdaptorType_Enum)
{
- if(GetType(args[DT_Menu_Adaptor_EnumSubmenu]) == C4V_Bool)
+ if(GetType(args.Enum.SubMenu.On) == C4V_Bool)
{
if(action & Menu_CallbackType_Defaults)
{
- return Menu_React_ShowSubmenu;
+ return Menu_React_ShowSubMenu;
}
else
{
return Menu_React_None;
}
}
-
- if(args[DT_Menu_Adaptor_EnumSubmenu] & action)
+ else if(args.Enum.SubMenu.On & action)
{
- return Menu_React_ShowSubmenu;
+ return Menu_React_ShowSubMenu;
}
- var enumVals = args[DT_Menu_Adaptor_LayoutVals];
- var layout = enumVals[1];
- enumVals = enumVals[0];
+ var enumVals = args.Enum.Values;
+ var layout = args.Enum.Layout;
- var index = EnumValPos(enumVals, layout, val, args[DT_Menu_Adaptor_EnumAllowUnknown]);
- var wrapAround = args[DT_Menu_Adaptor_WrapAround];
+ var index = EnumValPos(enumVals, layout, val, args.Enum.AllowUnknown);
+ var wrapAround = args.WrapAround;
if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2))
{
@@ -1490,18 +2080,18 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
index = WrapOrBind(index, [0, GetLength(enumVals) - 1], wrapAround);
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = val = enumVals[index][layout[Menu_Layout__ValuePos] - 1];
- reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs);
+ ScopedVar(args.Variable) = val = enumVals[index][layout[Menu_Layout__ValuePos] - 1];
+ reaction = AdaptorCommandCallChangedCallback(args.Callbacks, val, oldVal, allArgs);
if(reaction != Menu_React_None)
{
return reaction;
}
}
- else if(args[DT_Menu_Adaptor_Type] == Menu_AdaptorType_BitField)
+ else if(args.Type == Menu_AdaptorType_BitField)
{
if(action & (Menu_CallbackType_Normal | Menu_CallbackType_Special2))
{
- var mask = args[DT_Menu_Adaptor_Mask];
+ var mask = args.BitField.Mask;
if((val & mask) == mask)
{
val &= ~mask;
@@ -1511,8 +2101,8 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
val |= mask;
}
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = val;
- reaction = AdaptorCommandCallChangedCallback(args[DT_Menu_Adaptor_Callbacks], val, oldVal, allArgs);
+ ScopedVar(args.Variable) = val;
+ reaction = AdaptorCommandCallChangedCallback(args.Callbacks, val, oldVal, allArgs);
if(reaction != Menu_React_None)
{
return reaction;
@@ -1529,17 +2119,17 @@ func AdaptorCommand(int action, object obj, args, array allArgs)
func InputCallback(string input, int plr)
{
- var entry = entries[msgBoardEntry];
- var args = entry[DT_Menu_Entry_Args];
- var callbackArgs = args[DT_Menu_Adaptor_Args];
- var oldVal = ScopedVar(args[DT_Menu_Adaptor_Variable]);
+ var entry = columnEntries[msgBoardEntry[1]][msgBoardEntry[0]];
+ var args = entry.Args;
+ var callbackArgs = args.Args;
+ var oldVal = ScopedVar(args.Variable);
var val = input;
if(msgBoardMode - 1 == Menu_AdaptorType_Integer)
{
var int = ParseInt(input);
- var limits = AdaptorGetLimits(args[DT_Menu_Adaptor_Limits]);
- var step = args[DT_Menu_Adaptor_StepSize];
- if(GetType(int) == C4V_Int || GetType(int) == C4V_Any)
+ var limits = AdaptorGetLimits(args.Limits);
+ var step = args.StepSize;
+ if(GetType(int) == C4V_Int)
{
if(step && step[1])
{
@@ -1554,7 +2144,7 @@ func InputCallback(string input, int plr)
{
int = BoundBy(int, limits[0], limits[1]);
}
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = val = int;
+ ScopedVar(args.Variable) = val = int;
}
else
{
@@ -1563,13 +2153,13 @@ func InputCallback(string input, int plr)
}
else if(msgBoardMode - 1 == Menu_AdaptorType_String)
{
- if(args[DT_Menu_Adaptor_NoEmptyString] && (!input || input == ""))
+ if(args.NoEmptyString && (!input || input == ""))
{
val = oldVal;
}
else
{
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = input;
+ ScopedVar(args.Variable) = input;
}
}
else if(msgBoardMode - 1 == Menu_AdaptorType_ID)
@@ -1577,12 +2167,12 @@ func InputCallback(string input, int plr)
val = GetIDByName(input); // WARNING: desyncs between clients with different languages
if(!val && GetLength(input) == 4) val = C4Id(input);
if(!GetName(0, val)) val = 0;
- ScopedVar(args[DT_Menu_Adaptor_Variable]) = val;
+ ScopedVar(args.Variable) = val;
}
msgBoardMode = 0;
if(val != oldVal)
{
- var reaction = CallCallbacks(args[DT_Menu_Adaptor_Callbacks], Menu_CallbackType_ValueChanged, [Menu_CallbackType_ValueChanged, entry[DT_Menu_Entry_Symbol], settings[DT_Menu_Settings_Object], callbackArgs, 0, 0, val, oldVal]);
+ var reaction = CallCallbacks(args.Callbacks, Menu_CallbackType_ValueChanged, [Menu_CallbackType_ValueChanged, entry.Icon, settings.Object, callbackArgs, 0, 0, val, oldVal]);
if(reaction != Menu_React_None)
{
return React(reaction, msgBoardEntry);
@@ -1591,74 +2181,84 @@ func InputCallback(string input, int plr)
Refresh(msgBoardEntry);
}
-global func Menu_Combined(array combined) { return [DT_Menu_Combined, combined]; }
-
-global func CreateNewMenu(array menu, inheritSettings, object parentMenu)
-{
- var settings = inheritSettings;
-
- if(GetType(settings) != C4V_Array) settings = [];
- 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_Parent] = parentMenu;
-
- var entries = [];
-
- MN7I->UncombineAndDistinguish(menu, settings, entries);
+/*
+CreateNewMenu({
+ Settings = {
+ Icon = <Icon>,
+ Title = <Title>,
+ Object = <Object for which the menu is shown>,
+ Style = <Same as Style of CreateMenu>,
+ Size = [<Columns>, <Rows>] passed to SetMenuSize(),
+ Selecion = <Initial Selection>,
+ KeepOpen = <Keep Open Mode>: Menu_KeepOpen_*,
+ RefreshInterval = <Interval in Frames> for Menu_KeepOpen_RefreshContinuously,
+ Callbacks = [<Menu_Callback()...>] global Callbacks,
+ Decoration = <Decoration ID> passed to SetMenuDecoration(),
+ RequireAction = [<Action Name>] | [<Action Name>, <Action Target>] required action (and target) for the Settings.Object; if not fulfilled the menu will close itself,
+ KeepParentOnClose = <Should the parent menu stay open when the submenu closes>: true | >false<,
+ Vars = [<Initial values of MenuVar() ScopedVariables>...],
+ Closable = <Can the menu be closed by pressing dig / clicking the X in the corner>: true | >false<,
+ InstantDescription = <If enabled, shows the description of an entry immediately in a separate CustomMessage()>: {
+ Enable = true | >false<,
+ Decoration = <Decoration> passed to CustomMessage(); >DT_Menu_DefaultInstantDescriptionDecoration<,
+ Position = [<x>, <y>] passed to CustomMessage(); >[40, -60]<,
+ Flags = <Flags> passed to CustomMessage(); >MSG_Left | MSG_Bottom<
+ },
+ Extra = <Same as extra of CreateMenu>,
+ ExtraData = <Same as extraData of CreateMenu>,
+ Condition = {
+ DisableMode = <How to handle disabled entries>: >Menu_ConditionReact_Hide< | Menu_ConditionReact_*,
+ AllowSelection = <Allow disabled entries to be selected>: true | >false<
+ },
+ Parent = >Internal<: Used for submenus
+ },
+ Entries =
+ [Menu_Columns()] |
+ [<Menu_Entry() | Menu_Text() | Menu_Blank() | Menu_Decline() | Menu_Accept() | Menu_Back() | Menu_Adaptor() | | Menu_SubMenu() | Menu_Factory()>...]
+}) */
+
+global func CreateNewMenu(map settings, map inheritSettings, object parentMenu)
+{
+ inheritSettings = inheritSettings || {};
var menuObj = CreateObject(MN7I);
- menuObj->Create(settings, entries);
-
- return menuObj;
-}
-
-func NamedArg(array namedArg, array &args)
-{
- args[namedArg[0]] = namedArg[1];
-}
-
-func UncombineAndDistinguish(array combined, array &settings, array &entries)
-{
- for(var val in combined)
- {
- if(val[0] == DT_Menu_Type_Setting)
- {
- if((val[1][0] == DT_Menu_Settings_Style && val[1][1] == C4MN_Style_EqualItemHeight) || settings[DT_Menu_Settings_Style] == C4MN_Style_EqualItemHeight)
- {
- settings[DT_Menu_Settings_Style] |= val[1][1];
+ settings = Extend(Extend(Extend(
+ {
+ Settings = {
+ Condition = {
+ DisableMode = Menu_ConditionReact_Hide
+ },
+ InstantDescription = {
+ Decoration = DT_Menu_DefaultInstantDescriptionDecoration,
+ Flags = MSG_Left | MSG_Bottom,
+ Position = [40, -60]
+ },
+ KeepOpen = 0,
}
- else
- {
- NamedArg(val[1], settings);
+ }, { Settings = inheritSettings }, true), {
+ Settings = {
+ Selection = [], // Selection can't be inherited
+ Callbacks = [], // Global callbacks can't be inherited (maybe filter callbacks)
+ Parent = parentMenu
}
- }
- else if(val[0] == DT_Menu_Type_Entry || val[0] == DT_Menu_Type_Factory)
- {
- entries[GetLength(entries)] = val;
- }
- else if(val[0] == DT_Menu_Combined)
- {
- UncombineAndDistinguish(val[1], settings, entries);
- }
+ }, true),
+ settings, true);
+
+ if(GetType(settings.Settings.Selection) == C4V_Int)
+ {
+ settings.Settings.Selection = [settings.Settings.Selection, 0];
}
-}
-func NamedArgs(array namedArgs, array& args)
-{
- for(var arg in namedArgs)
+ if(inheritSettings.Style)
{
- if(arg[0] == DT_Menu_Combined)
- {
- NamedArgs(arg[1], args);
- }
- else
- {
- NamedArg(arg, args);
- }
+ // EqualItemHeight can't be inherited
+ settings.Settings.Style &= ~C4MN_Style_EqualItemHeight;
}
+
+ menuObj->Create(settings);
+
+ return menuObj;
}
func DeclineAcceptBackCommand(int action, args, int defaultAction, array allArgs)
@@ -1702,7 +2302,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
{
@@ -1736,20 +2336,121 @@ func CheckCustomScopedVar(array variable)
func Update()
{
- return Refresh(currentSelection);
+ return Refresh(GetSelection(Menu_Selection_WithColumn));
}
-func GetSelection()
+func GetSelection(int mode)
{
- return currentSelection;
+ mode ??= Menu_Selection_Simple;
+ if(mode == Menu_Selection_Simple)
+ {
+ return currentColumnSelections[currentColumn];
+ }
+ else if(mode == Menu_Selection_WithColumn)
+ {
+ return [currentColumnSelections[currentColumn], currentColumn];
+ }
+ else if(mode == Menu_Selection_SubMenuColumnChain)
+ {
+ if(multiColumnMode)
+ {
+ FatalError("DT_Menu::GetSelection: Menu_Selection_SubMenuColumnChain doesn't make sense when Menu_Columns() are used");
+ }
+
+ var ret = [];
+
+ for(var i = 0; i < columnEntries; ++i)
+ {
+ if(!columnEntries[i])
+ {
+ break;
+ }
+
+ ret[] = currentColumnSelections[i];
+ }
+
+ return ret;
+ }
}
func GetObject()
{
- return settings[DT_Menu_Settings_Object];
+ return settings.Object;
+}
+
+func CreateIconDummy()
+{
+ return CreateContents(DT_Menu_IconDummy);
+}
+
+// deprecated compatibility functions and constants
+// Symbol -> Icon
+static const Menu_Layout_Symbol = Menu_Layout_Icon;
+static const Menu_CallbackArg_Symbol = Menu_CallbackArg_Icon;
+
+
+func ValidateDeco(id deco)
+{
+ return !deco || (deco->~FrameDecorationBackClr() || deco->~FrameDecorationBorderTop() || deco->~FrameDecorationBorderLeft() || deco->~FrameDecorationBorderRight() || deco->~FrameDecorationBorderBottom());
}
-func CreateSymbolDummy()
+func ValidateMenuCallbacks(array callbacks)
{
- return CreateContents(DT_Menu_SymbolDummy);
-} \ No newline at end of file
+ if(!callbacks)
+ {
+ return false;
+ }
+
+ for(var callback in callbacks)
+ {
+ if(!ValidateMenuCallback(callback))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+func ValidateMenuCallbackArgBinding(array binding)
+{
+ for(var arg in binding)
+ {
+ if(arg > Menu_CallbackArg_Max || arg < Menu_CallbackArg_Min)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+func ValidateMenuCallback(array callback)
+{
+ if(!callback)
+ {
+ return false;
+ }
+
+ return GetLength(callback) == 3 && GetType(callback[1]) == C4V_Int && (callback[1] & ~Menu_CallbackType_All) == 0 && CheckCallback(callback[0]) && ValidateMenuCallbackArgBinding(callback[2]);
+}
+
+func CheckCallback(callback)
+{
+ var ret = _inherited(callback, ...);
+ if(ret && GetType(callback) == C4V_String)
+ {
+ Log("WARNING: DT_Menu::CheckCallback: A string only callback will not work as expected (yet): \"%s\"", callback);
+ }
+ return ret;
+}
+
+func CheckCustomCallback(callback)
+{
+ if(GetType(callback) == C4V_Array && GetLength(callback) == 2 && callback[0] == (CallbackTarget_Scenario - 1) && GetType(callback[1]) == C4V_String && callback[1] != "")
+ {
+ return true;
+ }
+
+ return _inherited(callback, ...);
+}
diff --git a/DTMenu.c4d/SymbolDummy.c4d/DefCore.txt b/DTMenu.c4d/SymbolDummy.c4d/DefCore.txt
deleted file mode 100644
index 1fd66a9..0000000
--- a/DTMenu.c4d/SymbolDummy.c4d/DefCore.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-[DefCore]
-id=MS7I
-Name=Menu symbol dummy
-Version=4,9,10,8
-Category=C4D_StaticBack
diff --git a/DTMenu.c4d/SymbolDummy.c4d/Script.c b/DTMenu.c4d/SymbolDummy.c4d/Script.c
deleted file mode 100644
index 4032726..0000000
--- a/DTMenu.c4d/SymbolDummy.c4d/Script.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#strict 2
-
-static const DT_Menu_SymbolDummy = MS7I;
-
-func SetSymbol(id symbol, int nr)
-{
- SetGraphics(0, this, symbol);
- SetPicture(GetDefCoreVal("Picture", "DefCore", symbol, 0) + nr * GetDefCoreVal("Picture", "DefCore", symbol, 2), GetDefCoreVal("Picture", "DefCore", symbol, 1), GetDefCoreVal("Picture", "DefCore", symbol, 2), GetDefCoreVal("Picture", "DefCore", symbol, 3));
- return this;
-}
-
-func SetColor(int color)
-{
- SetClrModulation(color);
- return this;
-}