Howto:Making a Canvas Menubar
Started in | 09/2014 |
---|---|
Description | A native Canvas menu bar |
Maintainer(s) | Hooray |
Contributor(s) | Jabberwocky[2], omega95[3] |
Status | Under active development as of 09/2014 |
Objective
This article or section contains out-of-date information
Please help improve this article by updating it. There may be additional information on the talk page. |
Demonstrate how to create a Canvas menubar and how to populate it by parsing $FG_ROOT/gui/menubar.xml using the readxml() API in io.nas and getValues() in props.nas.
We'll be prototyping the whole thing using the Nasal Console.
Roadmap
- Creating a Canvas Window Done
- Loading $FG_ROOT/gui/dialogs/menubar.xml vio readxml() from io.nas Done
- Processing the menubar as a Nasal hash via the pops.nas getValues() API Done
- Creating a function for creating menus and sub menus (see Omega95's code) Not done
- Adding support for mouse handling using Canvas Event Handling
- Adding support for bindings (fgcommands/nasal) Not done
- Full localization support Not done
- Use the Canvas Layout engine Not done
- Support widgets (checkboxes) in menus Not done
- Support adding menubar to an existing Canvas (for instruments/MFDs) Not done
- Support sub-menus Not done
- Add support for an optional tooltip tag for each menu/entry Not done
- Make the menu reloadable at run-time Not done
- Support multiple instances of a menubar
- Support multiple UI/menubar modes [4] Not done
- ...
- Add support for legacy styling system ? Not done
Background
I still have to find a way to move the menu more to the right ( center ) because in my current screen layout it is now in an invisble area (the center screen is higher than the screens left & right)
— pommesschranke (Sun Feb 23). Re: Multi screen configuration not working.
(powered by Instant-Cquotes) |
Changing the GUI/menubar would be extremely straightforward still - people can have their own menubars and even GUI styles, and the upcoming Canvas-based GUI post 3.2 is going to be even better style-able.
— Hooray (Wed Jun 25). Re: Sky to Dark at Night (was: Eye Candy & performance Issue.
(powered by Instant-Cquotes) |
The menubar and all options there are 100% XML-configurable using a PropertyList-dialect, much less sophisticated than any of the FDM/XML that experienced JSBSim developers -like yourself- are familiar with - so there's no reason why you shouldn't be able to edit $FG_ROOT/gui/menubar.xml accordingly.
— Hooray (Wed Jun 25). Re: Sky to Dark at Night (was: Eye Candy & performance Issue.
(powered by Instant-Cquotes) |
Yeah, the current problem is that the menu at is still handled in C++, and AFAIK the translated strings aren't exposed anywhere... I tried adding an extension function for the translation strings, but I was having problems with compiling/updating (and some other things). It's basically a one-line interface to globals->getLocale()->getTranslatedString(...). Otherwise the menubar is stored in the property tree - /sim/menubar/
— Philosopher (Sun Feb 23). Re: Multi screen configuration not working.
(powered by Instant-Cquotes) |
Code
Note In its current form, this is is to be copied and pasted into the Nasal Console for faster prototyping/testing. Eventually, this may be turned into a dedicated Nasal submodule or Canvas widget instead. For now, this ensures that the barrier to entry is kept sufficiently low.
To place all menus next to each other, we'd normally have to look up the bounding box for each previously added menu - but for the sake of simplicity we can also simply use the new Canvas Layout engine to arrange everything in a left-aligned horizontal box (hbox). Likewise, menu items in the popup menu can be arranged using a vbox layout. |
Work in progress This article or section will be worked on in the upcoming hours or days. See history for the latest developments. |
###
# Parent class for managing a Canvas
###
var CanvasApplication = {
##
# constructor
new: func(x=300, y=200) {
var m = { parents: [CanvasApplication] };
m.window = canvas.Window.new([x,y],"dialog");
m.canvas = m.window.createCanvas().setColorBackground(1,1,1,1);
m.root = m.canvas.createGroup();
m.init();
return m;
}, # new
init: func() {
}, #end of init
}; # end of CanvasApplication
###
# helper function for creating event handlers with a closure
###
var make_handler = func(group, menu, event, action) {
group.addEventListener(event, func() {
action(group, menu);
});
}; # make_handler()
###
# Helper function for creating text nodes
# and setting some defaults
###
var setupTextNode = func(group) {
return group.createChild("text")
.setDrawMode(canvas.Text.TEXT + canvas.Text.BOUNDINGBOX)
.setAlignment("left-top")
.setFontSize(18)
.setFont("LiberationFonts/LiberationSans-Regular.ttf")
.setColor(0,0,0);
};
var setupMenuGroup = func(menu) {
# now check for sub menus
foreach(var submenu; menu.item) {
if (submenu['enabled'] != 'false')
print("==> found new submenu item:", submenu.name);
} # get out items
}; # setupMenuGroup()
###
# Helper class for managing a menu bar
###
var CanvasMenubar = {
new: func(x,y, filename='gui/dialogs/menubar.xml') {
var m = {parents: [ CanvasMenubar, CanvasApplication.new(x,y) ], _filename:filename };
m.init();
return m;
},
init: func() {
me.load();
me.build();
},
del: func() {
},
load: func() {
var fgroot_path = getprop("/sim/fg-root");
var path = fgroot_path ~ '/' ~ me._filename;
# next, load menubar.xml using read_properties() (see io.nas) :
var menubar = io.read_properties( path );
# now, turn the whole thing into a Nasal hash for dead-simple processing
me.menubar = menubar.getValues();
},
build: func() {
var maxX=0;
foreach(var menu; me.menubar.menu) {
print("Found new menu:", menu.name);
# add some text to our menubar:
var myMenu = setupTextNode(me.root)
.setText(menu.name)
.setTranslation(maxX, 2); # hack (due to layout engine issue)
maxX += myMenu.getBoundingBox()[2]+20;
# set up a mouseover listener for the test object:
# change the text color based on mouse events:
make_handler(group:myMenu, menu:menu, event:"mouseenter", action: func(group) {group.setColor(1,0,0);} );
make_handler(group:myMenu, menu:menu, event:"mouseleave", action: func(group) {group.setColor(0,0,0);} );
setupMenuGroup(menu);
# respond to clicks
make_handler(group:myMenu, menu:menu, event:"click", action: func(group, menu) {
print("Menu clicked:",menu.name);
});
} # foreach menu
}, # build()
};
# support different kinds of menubars
var myMenubar = CanvasMenubar.new( x:getprop("/sim/startup/xsize"),
y:40, filename:'gui/menubar.xml');