Skip to content

Commit 41be9e3

Browse files
committed
Add main menu bar generation from gui panel paths
Bringing over all the gui code from the c++ version. Next up is documents & change tracking & save confirmation dialogs
1 parent 046d353 commit 41be9e3

File tree

7 files changed

+174
-16
lines changed

7 files changed

+174
-16
lines changed

src/Gui/Extensions.bf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,18 @@ namespace ImGui
5050
return ImGui.TreeNodeEx(str.Ptr, flags);
5151
}
5252

53+
public static bool MenuItem(StringView label, char* shortcut, bool* p_selected, bool enabled = true)
54+
{
55+
String str = scope .(label)..Append('\0');
56+
return ImGui.MenuItem(str.Ptr, shortcut, p_selected, enabled);
57+
}
58+
59+
public static bool BeginMenu(StringView label, bool enabled = true)
60+
{
61+
String str = scope .(label)..Append('\0');
62+
return ImGui.BeginMenu(str.Ptr, enabled);
63+
}
64+
5365
//Draw label and value next to each other with value using secondary color
5466
public static void LabelAndValue(StringView label, StringView value, Vec4<f32> color)
5567
{

src/Gui/Gui.bf

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Nanoforge.Gui
1111
[System]
1212
public class Gui : ISystem
1313
{
14-
public List<IGuiPanel> Panels = new .() ~DeleteContainerAndItems!(_);
14+
public List<GuiPanelBase> Panels = new .() ~DeleteContainerAndItems!(_);
1515

1616
static void ISystem.Build(App app)
1717
{
@@ -21,16 +21,37 @@ namespace Nanoforge.Gui
2121
[SystemInit]
2222
void Init(App app)
2323
{
24-
Panels.Add(new MainMenuBar());
25-
Panels.Add(new DebugGui());
26-
Panels.Add(new StateViewer());
24+
AddPanel("", true, new MainMenuBar());
25+
AddPanel("View/Debug", true, new DebugGui());
26+
AddPanel("View/State viewer", true, new StateViewer());
2727
}
2828

2929
[SystemStage(.Update)]
3030
void Update(App app)
3131
{
32-
for (IGuiPanel panel in Panels)
33-
panel.Update(app);
32+
for (GuiPanelBase panel in Panels)
33+
if (panel.Open)
34+
panel.Update(app, this);
3435
}
36+
37+
///Add gui panel and validate its path to make sure its not a duplicate. Takes ownership of panel.
38+
void AddPanel(StringView menuPos, bool open, GuiPanelBase panel)
39+
{
40+
//Make sure there isn't already a panel with the same menuPos
41+
for (GuiPanelBase existingPanel in Panels)
42+
{
43+
if (StringView.Equals(menuPos, existingPanel.MenuPos, true))
44+
{
45+
//Just crash when a duplicate is found. These are set at compile time currently so it'll always happen if theres a duplicate
46+
Runtime.FatalError(scope $"Duplicate GuiPanel menu path. Fix this before release. Type = {panel.GetType().ToString(.. scope .())}. Path = '{menuPos}'");
47+
delete panel;
48+
return;
49+
}
50+
}
51+
52+
panel.MenuPos.Set(menuPos);
53+
panel.Open = open;
54+
Panels.Add(panel);
55+
}
3556
}
3657
}

src/Gui/IGuiPanel.bf

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,23 @@ using System;
44

55
namespace Nanoforge.Gui
66
{
7-
//A single UI window. Only one instance of each implementation should exist
7+
///A single UI window. Only one instance of each implementation should exist
88
public interface IGuiPanel
99
{
1010
//Per-frame update
11-
public void Update(App app);
11+
public void Update(App app, Gui gui);
1212
}
13+
14+
///Base class for all gui panels. Has fields and functions that all panels should have.
15+
///Can't do this in IGuiPanel because Beef doesn't allow interfaces to have fields like we could get away with in C++.
16+
public class GuiPanelBase : IGuiPanel
17+
{
18+
public bool Open = true;
19+
public String MenuPos = new .() ~delete _;
20+
21+
public virtual void Update(App app, Gui gui)
22+
{
23+
return;
24+
}
25+
}
1326
}

src/Gui/Panels/DebugGui.bf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ using ImGui;
77

88
namespace Nanoforge.Gui.Panels
99
{
10-
public class DebugGui : IGuiPanel
10+
public class DebugGui : GuiPanelBase
1111
{
1212
bool ShowImGuiDemo = true;
1313

14-
public void Update(App app)
14+
public override void Update(App app, Gui gui)
1515
{
1616
if (ShowImGuiDemo)
1717
ImGui.ShowDemoWindow(&ShowImGuiDemo);
@@ -21,7 +21,7 @@ namespace Nanoforge.Gui.Panels
2121

2222
private void DrawDebugWindow(App app)
2323
{
24-
if (!ImGui.Begin(scope String(Icons.ICON_FA_BUG)..Append(" Debug")))
24+
if (!ImGui.Begin(scope String(Icons.ICON_FA_BUG)..Append(" Debug"), &Open))
2525
{
2626
ImGui.End();
2727
return;

src/Gui/Panels/MainMenuBar.bf

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,24 @@ using Nanoforge.App;
22
using Nanoforge;
33
using System;
44
using ImGui;
5+
using System.Collections;
56

67
namespace Nanoforge.Gui.Panels
78
{
8-
public class MainMenuBar : IGuiPanel
9+
public class MainMenuBar : GuiPanelBase
910
{
1011
private ImGui.DockNodeFlags dockspaceFlags = 0;
12+
public List<MenuItem> MenuItems = new .() ~DeleteContainerAndItems!(_);
1113

12-
void IGuiPanel.Update(App app)
14+
public override void Update(App app, Gui gui)
1315
{
16+
static bool firstDraw = true;
17+
if (firstDraw)
18+
{
19+
GenerateMenus(gui);
20+
firstDraw = false;
21+
}
22+
1423
DrawMainMenuBar(app);
1524
DrawDockspace(app);
1625
}
@@ -32,6 +41,11 @@ namespace Nanoforge.Gui.Panels
3241
{
3342
ImGui.EndMenu();
3443
}
44+
45+
//Draw menu item for each panel (e.g. file explorer, properties, log, etc) so user can toggle visibility
46+
for (MenuItem item in MenuItems)
47+
item.Draw();
48+
3549
if(ImGui.BeginMenu("View"))
3650
{
3751
//Todo: Put toggles for other guis in this layer here
@@ -87,5 +101,95 @@ namespace Nanoforge.Gui.Panels
87101

88102
ImGui.End();
89103
}
104+
105+
private MenuItem GetMenu(StringView text)
106+
{
107+
for (MenuItem item in MenuItems)
108+
if (StringView.Equals(item.Text, text, true))
109+
return item;
110+
111+
return null;
112+
}
113+
114+
private void GenerateMenus(Gui gui)
115+
{
116+
for (GuiPanelBase panel in gui.Panels)
117+
{
118+
//No menu entry if it's left blank. The main menu itself also doesn't have an entry
119+
if (panel.MenuPos == "" || panel == this)
120+
{
121+
panel.Open = true;
122+
continue;
123+
}
124+
125+
//Split menu path into components
126+
StringView[] pathSplit = panel.MenuPos.Split!('/');
127+
StringView menuName = pathSplit[0];
128+
129+
//Get or create menu
130+
MenuItem curMenuItem = GetMenu(menuName);
131+
if (curMenuItem == null)
132+
{
133+
curMenuItem = new MenuItem(menuName);
134+
MenuItems.Add(curMenuItem);
135+
}
136+
137+
//Iterate through path segmeents to create menu tree
138+
for (int i = 1; i < pathSplit.Count; i++)
139+
{
140+
StringView nextPart = pathSplit[i];
141+
MenuItem nextItem = curMenuItem.GetChild(nextPart);
142+
if (nextItem == null)
143+
{
144+
nextItem = new MenuItem(nextPart);
145+
curMenuItem.Items.Add(nextItem);
146+
}
147+
148+
curMenuItem = nextItem;
149+
}
150+
151+
curMenuItem.Panel = panel;
152+
}
153+
}
90154
}
155+
156+
//Entry in the main menu bar
157+
public class MenuItem
158+
{
159+
public String Text = new .() ~delete _;
160+
public List<MenuItem> Items = new .() ~DeleteContainerAndItems!(_);
161+
public GuiPanelBase Panel = null;
162+
163+
public this(StringView text)
164+
{
165+
Text.Set(text);
166+
}
167+
168+
public void Draw()
169+
{
170+
if (Panel != null)
171+
{
172+
ImGui.MenuItem(Text, "", &Panel.Open);
173+
return;
174+
}
175+
176+
if (ImGui.BeginMenu(Text))
177+
{
178+
for (MenuItem item in Items)
179+
{
180+
item.Draw();
181+
}
182+
ImGui.EndMenu();
183+
}
184+
}
185+
186+
public MenuItem GetChild(StringView text)
187+
{
188+
for (MenuItem item in Items)
189+
if (StringView.Equals(item.Text, text, true))
190+
return item;
191+
192+
return null;
193+
}
194+
}
91195
}

src/Gui/Panels/StateViewer.bf

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ using ImGui;
77
namespace Nanoforge.Gui.Panels
88
{
99
//Has a bunch of info about the apps state, system functions, and stages
10-
public class StateViewer : IGuiPanel
10+
public class StateViewer : GuiPanelBase
1111
{
12-
void IGuiPanel.Update(App app)
12+
public override void Update(App app, Gui gui)
1313
{
14-
if (!ImGui.Begin(scope String(Icons.ICON_FA_CODE_BRANCH)..Append(" App state")))
14+
if (!ImGui.Begin(scope String(Icons.ICON_FA_CODE_BRANCH)..Append(" App state"), &Open))
1515
{
1616
ImGui.End();
1717
return;

src/Misc/Extensions.bf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ namespace System
3737
return str;
3838
}
3939
}
40+
41+
extension StringView
42+
{
43+
public static bool Equals(StringView a, StringView b, bool ignoreCase = false)
44+
{
45+
return StringView.Compare(a, b, ignoreCase) == 0;
46+
}
47+
}
4048
}
4149

4250
static

0 commit comments

Comments
 (0)