NED, Tilemap Editor and NGUI Theme Editor were written in n7 using the ngui library. I just added the "widgets" and "containers" that I needed to this library as I created the editors. It may be hard to use if you've never worked with other gui libraries like IUP, Swing or WPF. But the principle is that you use containers to arrange widgets in different ways, you never position anything using exact coordinates. The library responds to window size changes by rearranging and resizing the containers and widgets. You can find the n7 source code of ngui in the N7/lib folder.
Here's a first example that creates some menus and labels.
Code:
' NGUI - Menus
' ------------
#win32
' This library is really large, so including it increases the compile time quite a bit.
include "ngui.n7"
' These will be explained later.
visible fruitLabel, gameLabel
' Create a resizable window.
set window "NGUI - Menus", 640, 480, false, 0
set redraw off
' Ngui uses containers to arrange widgets in different ways. The containers are widgets too.
' Every window needs a root widget, and it's usually a container. Here I create a vbox (vertical
' box), that aranges its children vertically. This will be the root widget of the window.
windowRoot = VBox(SIZE_EXPAND, SIZE_EXPAND)
' A menubar contains one or more menus. A menu has a title, like "File", and a bunch of children,
' like "Open", "Save" and "Save as".
' Create a menubar and add it to windowRoot.
menuBar = MenuBar()
windowRoot.Add(menuBar)
' Create a menu. The parameter is a function that will be called every time the user selects
' something in the menu. The function FruitMenuCallback is defined later in this program.
fruitMenu = Menu(FruitMenuCallback)
' Add some items to fruitMenu. The second parameter can be used to display something to the right
' of a menu item, usually a keyboard shortcut such as "Ctrl+N", but let's just skip that.
fruitMenu.Add("Banana", unset)
fruitMenu.Add("Apple", unset)
fruitMenu.Add("Pear", unset)
' Add fruitMenu to menuBar, the first parameter is the menu title.
menuBar.Add("Fruits", fruitMenu)
' Create another menu with another callback function.
gameMenu = Menu(GameMenuCallback)
gameMenu.Add("Crap's Adventure", unset)
gameMenu.Add("Bulb Boy", unset)
gameMenu.Add("Farmer Man", unset)
gameMenu.Add("Robowack", unset)
menuBar.Add("Games", gameMenu)
' Create another vbox and add it to the root.
vbox = VBox(SIZE_EXPAND, SIZE_EXPAND)
windowRoot.Add(vbox)
' Make this vbox center its children.
vbox.SetHalign(ALIGN_CENTER)
vbox.SetValign(ALIGN_CENTER)
' Add some text widgets to the vbox
vbox.Add(Header("Menus are super fun!", SIZE_AUTO, SIZE_AUTO))
vbox.Add(Label("Select stuff in the menus and things will happen", SIZE_AUTO, SIZE_AUTO))
' Add a filler, just empty space, of height 16.
vbox.Add(Filler(SIZE_AUTO, 16))
' Add two more labels, that will be modified from the menu callback functions.
fruitLabel = Label("No fruit selected", SIZE_AUTO, SIZE_AUTO)
gameLabel = Label("No game selected", SIZE_AUTO, SIZE_AUTO)
vbox.Add(fruitLabel)
vbox.Add(gameLabel)
' Enter main loop with windowRoot as root widget. From here on all action goes through the callback
' functions.
EnterMainLoop(windowRoot)
' Callback function for the fruit menu.
function FruitMenuCallback(menu, index)
select index
case 0 fruitLabel.SetText("I know how to spell banananananana but not when to quit")
case 1 fruitLabel.SetText("APPLE!!!")
case 2 fruitLabel.SetText("I don't remember what the third fruit was, sorry ...")
default fruitLabel.SetText("Undefined fruit encountered")
endsel
endfunc
' Callback function for the game menu.
function GameMenuCallback(menu, index)
select index
case 0, 1 gameLabel.SetText("You selected a platform game")
case 2 gameLabel.SetText("You selected a pacman clone")
case 3 gameLabel.SetText("You selected a first person shooter")
endsel
endfunc
11-19-2022, 08:27 AM (This post was last modified: 11-19-2022, 08:35 AM by Marcus.)
And here's an example showing buttons and message boxes.
Code:
' NGUI - Buttons
' --------------
#win32
' This library is really large, so including it increases the compile time quite a bit.
include "ngui.n7"
' These widgets will be created later.
visible fruitBox, programmingLanguageLabel
' Create a resizable window.
set window "NGUI - Buttons", 640, 480, false, 0
set redraw off
' Change the default font, used by most widgets.
SetDefaultFont("arial", 24)
' Change the header font, used by Header, ShowMessageBox and maybe some more widgets.
SetHeaderFont("arial", 26)
' Ngui uses containers to arrange widgets in different ways. The containers are widgets too.
' Every window needs a root widget, and it's usually a container. Here I create a vbox (vertical
' box), that aranges its children vertically. This will be the root widget of the window.
windowRoot = VBox(SIZE_EXPAND, SIZE_EXPAND)
windowRoot.SetHalign(ALIGN_CENTER)
windowRoot.SetValign(ALIGN_CENTER)
windowRoot.SetSpacing(4) ' Add some spacing between the children added to this container.
' Add a label to the root.
windowRoot.Add(Label("Fruits", SIZE_AUTO, SIZE_AUTO))
' Create a horizontal container and add it to the root.
fruitBox = HBox(SIZE_AUTO, SIZE_AUTO)
windowRoot.Add(fruitBox)
fruitBox.SetBorder(1) ' Add a 1 pixel visible border.
fruitBox.SetPadding(8) ' Add some padding between the content of the box and the border.
fruitBox.SetSpacing(4) ' Add some spacing between the buttons that we put in the box.
' Add three text buttons. The last parameter to TextButton is a function that will be called when
' the user clicks the button. The callback functions are defined later in this program.
fruitBox.Add(TextButton("Banana", SIZE_AUTO, SIZE_AUTO, BananaButtonCallback))
fruitBox.Add(TextButton("Apple", SIZE_AUTO, SIZE_AUTO, AppleButtonCallback))
fruitBox.Add(TextButton("Pear", SIZE_AUTO, SIZE_AUTO, PearButtonCallback))
' Add another group of buttons.
windowRoot.Add(Label("Programming languages", SIZE_AUTO, SIZE_AUTO))
' Instead of SIZE_AUTO, which minimizes the space used, use a fixed width of 320 pixels for this
' container.
hbox = HBox(320, SIZE_AUTO)
windowRoot.Add(hbox)
hbox.SetBorder(1)
hbox.SetPadding(8)
hbox.SetSpacing(4)
' Instead of SIZE_AUTO, set the width of every button to SIZE_EXPAND. In this case, it will make the
' hbox distribute its extra width (320) equally over all the buttons.
hbox.Add(TextButton("BASIC", SIZE_EXPAND, SIZE_AUTO, LanguageButtonCallback))
hbox.Add(TextButton("C#", SIZE_EXPAND, SIZE_AUTO, LanguageButtonCallback))
hbox.Add(TextButton("Prolog", SIZE_EXPAND, SIZE_AUTO, LanguageButtonCallback))
' Create an image for an ImageButton. I want the code to run without any other files, but one can
' of course use a loaded image instead.
img = createimage(128, 62)
set image img
for y = 0 to height(img) - 1 for x = 0 to width(img) - 1
set color 128 + rnd(128), 128 + rnd(128), 128 + rnd(128)
set pixel x, y
next
set image primary
' Add a label to the root.
windowRoot.Add(Label("Noise", SIZE_AUTO, SIZE_AUTO))
' Add an image button to the root.
windowRoot.Add(ImageButton(img, NoiseButtonCallback))
' Enter main loop with windowRoot as root widget. From here on all action goes through the callback
' functions.
EnterMainLoop(windowRoot)
' Callback function for the banana button.
function BananaButtonCallback(button)
' Change the background color of the banana button.
button.SetBackground([255, 220, 16])
endfunc
' Callback function for the apple button.
function AppleButtonCallback(button)
' Change the foreground (text) color of the apple button.
button.SetForeground([255, 32, 0])
endfunc
' Callback function for the pear button.
function PearButtonCallback(button)
' Change the bacground and foreground color of the pear button.
button.SetBackground([32, 255, 0])
button.SetForeground([192, 255, 128])
endfunc
' Callback function for the language buttons.
function LanguageButtonCallback(button)
' Show a standard message box with some text and two buttons. The last parameter to
' ShowMessageBox is a function that will be called when the user closes the dialog. If we
' weren't interested in the result we could have set the parameter to 'unset'.
' I haven't added a "getter" for TextButton text. So I'm doing something forbidden here by
' accessing the txt field of the button directly. Shh!
ShowMessageBox(
' Header.
"Warning",
' Message.
button.txt + " is a silly language!" + chr(10) + "You should stick with n7!",
' Buttons. We can add as many as we want, the index of the button will be sent to the
' callback function.
["Ok", "Refuse"],
LanguageDialogCallback)
endfunc
' Callback for the message box created in LanguageButtonCallback.
function LanguageDialogCallback(messageBox, buttonIndex)
' Show another dialog and terminate the program if the user refuses to stick with n7.
if buttonIndex = 1
ShowMessageBox("Alrighty then", "Have it your way, you silly poop!", ["Ok"],
' This time we use an anonymous function.
function(messageBox, buttonIndex)
end
endfunc)
endif
endfunc
' Callback function for the noise button.
function NoiseButtonCallback(button)
' Change the visibility of the fruit box.
fruitBox.SetHidden(not fruitBox.IsHidden())
endfunc
11-19-2022, 11:09 AM (This post was last modified: 11-19-2022, 11:10 AM by Marcus.)
And here comes check boxes and radio buttons. Each new example assumes that you've understood the earlier ones.
Code:
' NGUI - Check boxes and radio buttons
' ------------------------------------
#win32
' This library is really large, so including it increases the compile time quite a bit.
include "ngui.n7"
' These widgets will be created later.
visible bananaCheckBox, appleCheckBox, pearCheckBox
visible languageLabel, languageTextEditor
' Create a resizable window.
set window "NGUI - Check boxes and radio buttons", 640, 480, false, 0
set redraw off
' Ngui uses containers to arrange widgets in different ways. The containers are widgets too.
' Every window needs a root widget, and it's usually a container. Here I create a hbox (horizontal
' box), that aranges its children horizontally. This will be the root widget of the window.
windowRoot = HBox(SIZE_EXPAND, SIZE_EXPAND)
windowRoot.SetPadding(4) ' Some distance to the window border.
windowRoot.SetSpacing(8) ' Spacing between children.
' Create a vbox to put some checkboxes and other stuff in and add it to the root.
vbox = VBox(SIZE_EXPAND, SIZE_AUTO)
windowRoot.Add(vbox)
vbox.SetSpacing(4)
' Add a header.
vbox.Add(Header("Check boxes", SIZE_EXPAND, SIZE_AUTO))
' Last is a function in ngui that simply returns the last element of an array. GetChildren returns
' an array with all the children of the vbox. So, we're calling a function, SetHalign, of the last
' widget added to the vbox, which was the header. SetAlign centers the text WITHIN the label. You
' see, the width of the label is SIZE_EXPAND, so it will get the width of the vbox.
Last(vbox.GetChildren()).SetHalign(ALIGN_CENTER)
' Create a checkbox with a label attached to it. What the (misspelled) function LabledCheckBox
' ACTUALLY returns is a hbox containing a CheckBox and a Label. The last parameter to LabledCheckBox
' is a function that will be called when the user clicks the checkbox. The second parameter, false,
' makes the checkbox unchecked.
vbox.Add(LabledCheckBox("Use dark mode", false, DarkModeCheckBoxCallback))
' Add a horizontal divider, just a horizontal line, of the height 1 and vertical padding 2.
vbox.Add(HDivider(1, 2))
' Add some more checkboxes and store them in global variables. We'll look at them in the callback
' function of a button created bellow.
bananaCheckBox = LabledCheckBox("Banana", false, unset)
vbox.Add(bananaCheckBox)
appleCheckBox = LabledCheckBox("Apple", false, unset)
vbox.Add(appleCheckBox)
pearCheckBox = LabledCheckBox("Pear", false, unset)
vbox.Add(pearCheckBox)
' Add a text button with the callback function ConfirmFruitsButtonCallback.
vbox.Add(TextButton("Confirm fruits", SIZE_EXPAND, SIZE_AUTO, ConfirmFruitsButtonCallback))
' Create another vbox, for some radio button groups and other stuff, and add it to the root.
vbox = VBox(SIZE_EXPAND, SIZE_EXPAND)
windowRoot.Add(vbox)
vbox.SetSpacing(4)
vbox.Add(Header("Radio buttons", SIZE_EXPAND, SIZE_AUTO))
Last(vbox.GetChildren()).SetHalign(ALIGN_CENTER)
' Radio buttons belong to groups. Only one radio button in a group can be selected. The group, just
' a string, is the first parameter to the (misspelled) function LabledRadioButton. Other than the
' group, the arguments are the same as those to LabledCheckBox. Set the callback function to
' FruitsRadioButtonCallback.
vbox.Add(LabledRadioButton("fruits", "Apple", false, FruitsRadioButtonCallback))
vbox.Add(LabledRadioButton("fruits", "Banana", false, FruitsRadioButtonCallback))
vbox.Add(LabledRadioButton("fruits", "Pear", false, FruitsRadioButtonCallback))
vbox.Add(LabledRadioButton("fruits", "None", true, FruitsRadioButtonCallback))
vbox.Add(HDivider(1, 2))
' Create another radio button group but don't provide a callback function. The selected item will
' be checked when the user presses a button, created later.
vbox.Add(LabledRadioButton("languages", "BASIC", false, unset))
vbox.Add(LabledRadioButton("languages", "C#", false, unset))
vbox.Add(LabledRadioButton("languages", "C", false, unset))
vbox.Add(LabledRadioButton("languages", "N7", true, unset))
' Confirmation button for language selection.
vbox.Add(TextButton("Confirm language", SIZE_EXPAND, SIZE_AUTO, ConfirmLanguageButtonCallback))
' A label that will be set when the user selects a radio button in the fruits group.
languageLabel = Label("no source code", SIZE_EXPAND, SIZE_AUTO)
vbox.Add(languageLabel)
' Add a text editor. Its content will be changed when the user confirms the language, that is in
' the ConfirmLanguageButtonCallback function.
languageTextEditor = TextEditor(SIZE_EXPAND, SIZE_EXPAND)
vbox.Add(languageTextEditor)
' Enter main loop with windowRoot as root widget. From here on all action goes through the callback
' functions.
EnterMainLoop(windowRoot)
' Callback function for dark mode button.
function DarkModeCheckBoxCallback(checkBox, checked)
' Switch between dark and light mode.
SetDarkMode(checked)
endfunc
' Callback function for the fruit confirmation button.
function ConfirmFruitsButtonCallback(button)
' The check boxes we created using LabledCheckBox are containers with a CheckBox and Label each.
' I must remember to add a IsChecked function to these containers in the next update. For now
' you have to call GetCheckBox to get the actual checkbox associated with the LabledCheckBox.
selectedCount = appleCheckBox.GetCheckBox().IsChecked()
selectedCount = selectedCount + bananaCheckBox.GetCheckBox().IsChecked()
selectedCount = selectedCount + pearCheckBox.GetCheckBox().IsChecked()
' Message for a message box.
if selectedCount = 0 msg = "No fruits selected."
elseif selectedCount = 1 msg = "You selected 1 fruit."
else msg = "You selected " + selectedCount + " fruits."
' Show a message box with an ok button.
ShowMessageBox("Confirm", msg, ["Ok"], unset)
endfunc
' Callback function for the fruit radio buttons.
function FruitsRadioButtonCallback(radioButton, index)
' Change the text of the language label.
select index
case 0 languageLabel.SetText("applesauce code")
case 1 languageLabel.SetText("banana source code")
case 2 languageLabel.SetText("pear source code")
case 3 languageLabel.SetText("no source code")
endsel
endfunc
' Callback for language confirmation button.
function ConfirmLanguageButtonCallback(button)
' Clear the content of the text editor.
languageTextEditor.NewDocument()
' Clear the keyword list of the text editor.
languageTextEditor.ClearKeywords()
' We can get the selected radio button index for a specific group using
' SelectedRadioButtonIndex.
select SelectedRadioButtonIndex("languages")
case 0 ' BASIC
' Add a single keyword to the text editor (this enables syntax highlighting).
languageTextEditor.AddKeyword("PRINT", 0)
' Add a line of BASIC code.
languageTextEditor.AddLine("PRINT " + chr(34) + "Hello world!" + chr(34))
case 1 ' C#
' Gotta add two keywords for this one.
languageTextEditor.AddKeyword("Trace", 0)
languageTextEditor.AddKeyword("WriteLine", 0)
languageTextEditor.AddLine("Trace.WriteLine(" + chr(34) + "Hello world!" + chr(34) + ");")
case 2 ' C
languageTextEditor.AddKeyword("printf", 0)
languageTextEditor.AddLine("printf(" + chr(34) + "Hello world!" + chr(34) + ");")
case 3 ' N7
languageTextEditor.AddKeyword("pln", 0)
languageTextEditor.AddLine("pln " + chr(34) + "Hello world!" + chr(34))
endsel
endfunc
11-19-2022, 01:52 PM (This post was last modified: 11-19-2022, 01:52 PM by Marcus.)
I haven't explained the SIZE_EXPAND and SIZE_AUTO constants. They're used when creating widgets as width and height parameters. SIZE_EXPAND means, sort of like, "expand to fit the available space". SIZE_AUTO means "fit to content" or "make as small as possible". You can always set sizes in pixels instead of using these constants.
11-26-2022, 08:24 AM (This post was last modified: 11-26-2022, 08:30 AM by Marcus.)
An example of ComboBox and ListBox, two ways of letting the user select stuff from lists.
Code:
' NGUI - ComboBox and ListBox
' ---------------------------
#win32
' This library is really large, so including it increases the compile time quite a bit.
include "ngui.n7"
' These widgets will be created later.
visible fruitLabel, stateLabel, languageLabel
' Create a resizable window.
set window "NGUI - ComboBox and ListBox", 640, 480, false, 0
set redraw off
' Ngui uses containers to arrange widgets in different ways. The containers are widgets too.
' Every window needs a root widget, and it's usually a container. Here I create a hbox (horizontal
' box), that aranges its children horizontally. This will be the root widget of the window.
windowRoot = HBox(SIZE_EXPAND, SIZE_EXPAND)
windowRoot.SetPadding(4) ' Some distance to the window border.
windowRoot.SetSpacing(8) ' Spacing between children.
' Create a vbox for a combo box and a label.
vbox = VBox(SIZE_EXPAND, SIZE_EXPAND)
windowRoot.Add(vbox)
vbox.SetHalign(ALIGN_CENTER)
vbox.SetSpacing(16)
' Create a combo box. For an unknown reason its width is the first parameter. And for some other
' unknown reason (probably I was in a rush) SIZE_EXPAND doesn't work, so let's use a fixed with of
' 88 pixels. The second parameter is the content that will be shown for selection in a drop down
' menu. The third parameter is a callback function defined later.
cb = ComboBox(88, ["Apple", "Banana", "Pear", "Blue car"], ComboBoxCallback)
' If nothing has been selected, the default text is "Select". We can change the default text with
' SetDefaultText.
cb.SetDefaultText("Select fruit")
vbox.Add(cb)
' Create a label that will be modified in ComboBoxCallback.
fruitLabel = Label("", SIZE_AUTO, SIZE_AUTO)
vbox.Add(fruitLabel)
' Create a vbox for a list box with a scrollbar and a label.
vbox = VBox(SIZE_EXPAND, SIZE_EXPAND)
windowRoot.Add(vbox)
vbox.SetHalign(ALIGN_CENTER)
vbox.SetSpacing(16)
' We can connect a vertical scrollbar to a list box. Therefor we create a hbox for both these
' widgets.
hbox = HBox(SIZE_AUTO, SIZE_EXPAND)
vbox.Add(hbox)
' A list of items for our list box.
items = ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut",
"Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa",
"Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan",
"Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire",
"New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio",
"Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota",
"Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia",
"Wisconsin", "Wyoming"]
' The first parameter to a list box is its content, and the fourth is a callback function.
lb = ListBox(items, SIZE_AUTO, SIZE_EXPAND, ListBoxCallback)
hbox.Add(lb)
' Create and add a scrollbar to the hbox. The first parameter to VerticalScrollbar is the widget to
' which we want to connect it. A widget needs to actually support a scrollbar for this to work, and
' list boxes do.
hbox.Add(VerticalScrollbar(lb, SIZE_AUTO, SIZE_EXPAND))
' Create a label that will be modified in ListBoxCallback.
stateLabel = Label("", SIZE_AUTO, SIZE_AUTO)
vbox.Add(stateLabel)
' Create a vbox for another combo box and a label.
vbox = VBox(SIZE_EXPAND, SIZE_AUTO)
windowRoot.Add(vbox)
vbox.SetHalign(ALIGN_CENTER)
vbox.SetSpacing(16)
' The list use for this combobox will be one of tables.
languages = [
[name: "N7", author: "Marcus"],
[name: "micro(A)", author: "Aurel"],
[name: "Brainfuck", author: "Urban"],
[name: "FLOW-MATIC", author: "Grace"]]
' Create the combobox and put it in the vbox-
cb = ComboBox(88, languages, SecondComboBoxCallback)
vbox.Add(cb)
' When dealing with a list of tables we have to tell the combo box which field to display in the
' list.
cb.SetDisplayField("name")
' Create a label that will be modified in SecondComboBoxCallback.
languageLabel = Label("", SIZE_AUTO, SIZE_AUTO)
vbox.Add(languageLabel)
languageLabel.SetHalign(ALIGN_CENTER)
' Enter main loop with windowRoot as root widget. From here on all action goes through the callback
' functions.
EnterMainLoop(windowRoot)
' Callback for first combo box.
function ComboBoxCallback(comboBox, selectedIndex)
fruitLabel.SetText(comboBox.GetSelectedItem())
endfunc
' Callback for list box.
function ListBoxCallback(listBox, selectedIndexes)
' A list box actually supports selection of multiple items. If enabled, selectedIndexes will be
' an array of indexes, but now it's just a number.
stateLabel.SetText(listBox.GetItems()[selectedIndexes])
endfunc
' Callback for second combo box.
function SecondComboBoxCallback(comboBox, selectedIndex)
item = comboBox.GetSelectedItem()
languageLabel.SetText(item.name + chr(10) + "by" + chr(10) + item.author)
endfunc