Add global canvas menu bar
This commit is contained in:
parent
edf4077402
commit
ac4b64a01d
4 changed files with 357 additions and 64 deletions
|
@ -14,7 +14,6 @@ var gui = {
|
|||
focused_window: nil,
|
||||
open_popups: [],
|
||||
region_highlight: nil,
|
||||
menubar: nil,
|
||||
|
||||
# Window/dialog stacking order
|
||||
STACK_INDEX: {
|
||||
|
@ -31,6 +30,8 @@ var loadDialog = func(name) loadGUIFile("dialogs/" ~ name ~ ".nas");
|
|||
|
||||
loadGUIFile("Config.nas");
|
||||
loadGUIFile("Menu.nas");
|
||||
loadGUIFile("MenuBar.nas");
|
||||
loadGUIFile("Overlay.nas");
|
||||
loadGUIFile("Popup.nas");
|
||||
loadGUIFile("Style.nas");
|
||||
loadGUIFile("Widget.nas");
|
||||
|
@ -606,21 +607,14 @@ getDesktop().addEventListener("mousedown", func {
|
|||
}
|
||||
});
|
||||
|
||||
# disabled until this is hooked up to the PUICompat code
|
||||
#gui.menubar = gui.MenuBar.new();
|
||||
|
||||
# Provide old 'Dialog' for backwards compatiblity (should be removed for 3.0)
|
||||
var Dialog = {
|
||||
new: func(size, type = nil, id = nil)
|
||||
{
|
||||
debug.warn("'canvas.Dialog' is deprectated! (use canvas.Window instead)");
|
||||
debug.warn("'canvas.Dialog' is deprecated! (use canvas.Window instead)");
|
||||
return Window.new(size, type, id);
|
||||
}
|
||||
};
|
||||
|
||||
var unloadGUI = func() {
|
||||
if (gui.menubar) {
|
||||
gui.menubar.del();
|
||||
gui.menubar = nil;
|
||||
}
|
||||
}
|
||||
|
|
58
Nasal/canvas/gui/MenuBar.nas
Normal file
58
Nasal/canvas/gui/MenuBar.nas
Normal file
|
@ -0,0 +1,58 @@
|
|||
gui.MenuBar = {
|
||||
new: func() {
|
||||
var obj = {
|
||||
parents: [gui.MenuBar, canvas.gui.Overlay.new([24, 24], "global-menu-bar")],
|
||||
};
|
||||
|
||||
obj._canvas = obj.createCanvas().setColorBackground(canvas.style.getColor("bg_color"));
|
||||
obj._root = obj._canvas.createGroup();
|
||||
obj._layout = canvas.VBoxLayout.new();
|
||||
obj._canvas.setLayout(obj._layout);
|
||||
obj._menuBar = canvas.gui.widgets.MenuBar.new(obj._root, canvas.style, {});
|
||||
obj._layout.addItem(obj._menuBar);
|
||||
|
||||
var wrapSizeChangingWidgetFunc = func(sizeChangingWidgetFuncName) {
|
||||
var sizeChangingWidgetFunc = gui.widgets.MenuBar[sizeChangingWidgetFuncName];
|
||||
var wrapperFunc = func {
|
||||
var result = call(sizeChangingWidgetFunc, arg, obj._menuBar);
|
||||
obj.updateSize();
|
||||
return result;
|
||||
}
|
||||
obj[sizeChangingWidgetFuncName] = wrapperFunc;
|
||||
}
|
||||
var wrapWidgetFunc = func(widgetFuncName) {
|
||||
var widgetFunc = gui.widgets.MenuBar[widgetFuncName];
|
||||
var wrapperFunc = func {
|
||||
return call(widgetFunc, arg, obj._menuBar);
|
||||
}
|
||||
obj[widgetFuncName] = wrapperFunc;
|
||||
}
|
||||
|
||||
wrapSizeChangingWidgetFunc("addMenu");
|
||||
wrapSizeChangingWidgetFunc("createMenu");
|
||||
wrapSizeChangingWidgetFunc("clear");
|
||||
wrapSizeChangingWidgetFunc("removeMenu");
|
||||
wrapSizeChangingWidgetFunc("takeAt");
|
||||
wrapWidgetFunc("count");
|
||||
wrapWidgetFunc("getItem");
|
||||
wrapWidgetFunc("getMenu");
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
setSize: func(s) {
|
||||
call(me.parents[1].setSize, [s], me);
|
||||
me._menuBar.setSize(s);
|
||||
return me;
|
||||
},
|
||||
|
||||
updateSize: func {
|
||||
me.setSize(me._menuBar._size);
|
||||
},
|
||||
|
||||
update: func {
|
||||
me._menuBar.update();
|
||||
return me;
|
||||
},
|
||||
};
|
||||
|
245
Nasal/canvas/gui/Overlay.nas
Normal file
245
Nasal/canvas/gui/Overlay.nas
Normal file
|
@ -0,0 +1,245 @@
|
|||
# SPDX-FileCopyrightText: (C) 2022 Frederic Croix <thefgfseagle@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
gui.Overlay = {
|
||||
# Constructor
|
||||
#
|
||||
# @param size ([width, height])
|
||||
new: func(size_, id = nil) {
|
||||
var ghost = _newWindowGhost(id);
|
||||
var m = {
|
||||
parents: [gui.Overlay, PropertyElement, ghost],
|
||||
_ghost: ghost,
|
||||
_node: props.wrapNode(ghost._node_ghost),
|
||||
_widgets: [],
|
||||
_canvas: nil,
|
||||
_focused: 1,
|
||||
};
|
||||
|
||||
m.setSize(size_);
|
||||
m._updateDecoration();
|
||||
|
||||
# arg = [child, listener_node, mode, is_child_event]
|
||||
setlistener(m._node, func m._propCallback(arg[0], arg[2]), 0, 2);
|
||||
|
||||
return m;
|
||||
},
|
||||
# Destructor
|
||||
del: func {
|
||||
if (me["_canvas"] != nil) {
|
||||
var placements = me._canvas._node.getChildren("placement");
|
||||
# Do not remove canvas if other placements exist
|
||||
if (size(placements) > 1) {
|
||||
foreach (var p; placements) {
|
||||
if (p.getValue("type") == "window" and p.getValue("id") == me.get("id")) {
|
||||
p.remove();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
me._canvas.del();
|
||||
}
|
||||
me._canvas = nil;
|
||||
}
|
||||
if (me._node != nil) {
|
||||
me._node.remove();
|
||||
me._node = nil;
|
||||
}
|
||||
},
|
||||
# Create the canvas to be used for this Window
|
||||
#
|
||||
# @return The new canvas
|
||||
createCanvas: func() {
|
||||
var size = [
|
||||
me.get("content-size[0]"),
|
||||
me.get("content-size[1]")
|
||||
];
|
||||
|
||||
me._canvas = new({
|
||||
size: [size[0], size[1]],
|
||||
view: size,
|
||||
placement: {
|
||||
type: "window",
|
||||
id: me.get("id")
|
||||
},
|
||||
|
||||
# Standard alpha blending
|
||||
"blend-source-rgb": "src-alpha",
|
||||
"blend-destination-rgb": "one-minus-src-alpha",
|
||||
|
||||
# Just keep current alpha (TODO allow using rgb textures instead of rgba?)
|
||||
"blend-source-alpha": "zero",
|
||||
"blend-destination-alpha": "one"
|
||||
});
|
||||
|
||||
me._canvas._focused_widget = nil;
|
||||
me._canvas.data("focused", me._focused);
|
||||
|
||||
return me._canvas;
|
||||
},
|
||||
# Set an existing canvas to be used for this Window
|
||||
setCanvas: func(canvas_) {
|
||||
if (ghosttype(canvas_) != "Canvas") {
|
||||
return debug.warn("Not a Canvas");
|
||||
}
|
||||
|
||||
canvas_.addPlacement({type: "window", "id": me.get("id")});
|
||||
me['_canvas'] = canvas_;
|
||||
|
||||
canvas_._focused_widget = nil;
|
||||
canvas_.data("focused", me._focused);
|
||||
|
||||
return me;
|
||||
},
|
||||
# Get the displayed canvas
|
||||
getCanvas: func(create = 0) {
|
||||
if (me['_canvas'] == nil and create) {
|
||||
me.createCanvas();
|
||||
}
|
||||
|
||||
return me['_canvas'];
|
||||
},
|
||||
setLayout: func(l) {
|
||||
if (me['_canvas'] == nil) {
|
||||
me.createCanvas();
|
||||
}
|
||||
|
||||
me._canvas.update(); # Ensure placement is applied
|
||||
me._ghost.setLayout(l);
|
||||
return me;
|
||||
},
|
||||
setPosition: func {
|
||||
if (size(arg) == 1) {
|
||||
var arg = arg[0];
|
||||
}
|
||||
var (x, y) = arg;
|
||||
|
||||
me.setInt("tf/t[0]", x);
|
||||
me.setInt("tf/t[1]", y);
|
||||
return me;
|
||||
},
|
||||
setSize: func {
|
||||
if (size(arg) == 1) {
|
||||
var arg = arg[0];
|
||||
}
|
||||
var (w, h) = arg;
|
||||
|
||||
me.set("content-size[0]", w);
|
||||
me.set("content-size[1]", h);
|
||||
|
||||
if (me["_canvas"] == nil) {
|
||||
print("canvas is nil");
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2; i += 1) {
|
||||
var size = me.get("content-size[" ~ i ~ "]");
|
||||
me._canvas.set("size[" ~ i ~ "]", size);
|
||||
me._canvas.set("view[" ~ i ~ "]", size);
|
||||
}
|
||||
|
||||
return me;
|
||||
},
|
||||
getSize: func {
|
||||
var w = me.get("content-size[0]");
|
||||
var h = me.get("content-size[1]");
|
||||
return [w,h];
|
||||
},
|
||||
# Raise to top of window stack
|
||||
raise: func() {
|
||||
# on writing the z-index the window always is moved to the top of all other
|
||||
# windows with the same z-index.
|
||||
me.setInt("z-index", me.get("z-index", gui.STACK_INDEX["always-on-top"]));
|
||||
},
|
||||
hide: func(parents = 0) {
|
||||
me._ghost.hide();
|
||||
},
|
||||
show: func() {
|
||||
me._ghost.show();
|
||||
me.raise();
|
||||
if (me._canvas != nil) {
|
||||
me._canvas.update();
|
||||
}
|
||||
},
|
||||
# Hide / show the window based on whether it's currently visible
|
||||
toggle: func() {
|
||||
if (me.isVisible()) {
|
||||
me.hide();
|
||||
} else {
|
||||
me.show();
|
||||
}
|
||||
},
|
||||
_onStateChange: func {
|
||||
var event = canvas.CustomEvent.new("wm.focus-" ~ (me._focused ? "in" : "out"));
|
||||
|
||||
if (me.getCanvas() != nil) {
|
||||
me.getCanvas().data("focused", me._focused).dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
#mode 0 = value changed, +-1 add/remove node
|
||||
_propCallback: func(child, mode) {
|
||||
if (!me._node.equals(child.getParent())) {
|
||||
return;
|
||||
}
|
||||
var name = child.getName();
|
||||
|
||||
# support for CSS like position: absolute; with right and/or bottom margin
|
||||
if (name == "right") {
|
||||
me._handlePositionAbsolute(child, mode, name, 0);
|
||||
} elsif (name == "bottom") {
|
||||
me._handlePositionAbsolute(child, mode, name, 1);
|
||||
}
|
||||
|
||||
if (mode == 0) {
|
||||
if (name == "size") {
|
||||
me._resizeDecoration();
|
||||
}
|
||||
}
|
||||
},
|
||||
_handlePositionAbsolute: func(child, mode, name, index) {
|
||||
# mode
|
||||
# -1 child removed
|
||||
# 0 value changed
|
||||
# 1 child added
|
||||
|
||||
if (mode == 0) {
|
||||
me._updatePos(index, name);
|
||||
} elsif (mode == 1) {
|
||||
me["_listener_" ~ name] = [
|
||||
setlistener(
|
||||
"/sim/gui/canvas/size[" ~ index ~ "]",
|
||||
func me._updatePos(index, name)
|
||||
),
|
||||
setlistener(
|
||||
me._node.getNode("content-size[" ~ index ~ "]"),
|
||||
func me._updatePos(index, name)
|
||||
)
|
||||
];
|
||||
} elsif (mode == -1) {
|
||||
for (var i = 0; i < 2; i += 1) {
|
||||
removelistener(me["_listener_" ~ name][i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
_updatePos: func(index, name) {
|
||||
me.setInt(
|
||||
"tf/t[" ~ index ~ "]",
|
||||
getprop("/sim/gui/canvas/size[" ~ index ~ "]") - me.get(name) - me.get("content-size[" ~ index ~ "]")
|
||||
);
|
||||
},
|
||||
getCanvasDecoration: func() {
|
||||
return wrapCanvas(me._getCanvasDecoration());
|
||||
},
|
||||
_updateDecoration: func() {
|
||||
me.setBool("update", 1);
|
||||
|
||||
#var canvas_deco = me.getCanvasDecoration();
|
||||
#var group_deco = canvas_deco.getGroup("decoration");
|
||||
|
||||
me._resizeDecoration();
|
||||
me._onStateChange();
|
||||
},
|
||||
_resizeDecoration: func() {
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -1,60 +1,56 @@
|
|||
# SPDX-FileCopyrightText: (C) 2022 James Turner <james@flightgear.org>
|
||||
# SPDX-FileCopyrightText: (C) 2023 TheFGFSEagle <thefgfseagle@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
var menubar = nil;
|
||||
|
||||
var GUIMenuItem = {
|
||||
|
||||
aboutToShow: func() {
|
||||
|
||||
},
|
||||
|
||||
# return a Canvas object (group) of the contents
|
||||
show: func(viewParent) {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var GUIMenu = {
|
||||
|
||||
aboutToShow: func() {
|
||||
|
||||
},
|
||||
|
||||
# return a Canvas object (group) of the contents
|
||||
show: func(viewParent) {
|
||||
# loop over children
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
var GUIMenuBar = {
|
||||
aboutToShow: func() {
|
||||
|
||||
},
|
||||
|
||||
# return a Canvas object (group) of the contents
|
||||
show: func(viewParent) {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
# this is the callback function invoked by C++ to build Nasal peers
|
||||
# for the C++ menu objects.
|
||||
var _createMenuObject = func(type)
|
||||
{
|
||||
if (type == "menubar") {
|
||||
|
||||
} else if (type == "menuitem") {
|
||||
|
||||
} else if (type == "seperator") {
|
||||
|
||||
} else if (type == "menu") {
|
||||
# do we need to distuinguish submenus here:
|
||||
}
|
||||
|
||||
return nil;
|
||||
var _addItem = func(parent, itemGhost) {
|
||||
var item = parent.createItem(
|
||||
text: itemGhost.label,
|
||||
cb: itemGhost.fire,
|
||||
cb_me: itemGhost,
|
||||
shortcut: itemGhost.shortcut,
|
||||
enabled: itemGhost.enabled,
|
||||
);
|
||||
}
|
||||
|
||||
var _addMenu = func(parent, menuGhost) {
|
||||
var menu = parent.createMenu(menuGhost.label);
|
||||
foreach (var item; menuGhost.items) {
|
||||
_addItem(menu, item);
|
||||
}
|
||||
}
|
||||
|
||||
var _createMenuBar = func(menubarGhost) {
|
||||
if (menubar != nil) {
|
||||
menubar.del();
|
||||
menubar = nil;
|
||||
}
|
||||
menubar = canvas.gui.MenuBar.new();
|
||||
foreach (var menu; menubarGhost.menus) {
|
||||
_addMenu(menubar, menu);
|
||||
}
|
||||
menubar.show();
|
||||
}
|
||||
|
||||
var _destroyMenuBar = func(menubarGhost) {
|
||||
if (menubar != nil) {
|
||||
return;
|
||||
}
|
||||
menubar.del();
|
||||
menubar = nil;
|
||||
}
|
||||
|
||||
var _showMenuBar = func(menubarGhost) {
|
||||
if (menubar == nil) {
|
||||
return;
|
||||
}
|
||||
menubar.show();
|
||||
}
|
||||
|
||||
var _hideMenuBar = func(menubarGhost) {
|
||||
if (menubar == nil) {
|
||||
return;
|
||||
}
|
||||
menubar.hide();
|
||||
}
|
||||
|
||||
logprint(LOG_INFO, "Did load GUI menubar");
|
||||
|
|
Loading…
Reference in a new issue