Added Popup, Menu and MenuItem classes
This commit is contained in:
parent
581f8d163f
commit
764e6f0ced
6 changed files with 678 additions and 32 deletions
|
@ -12,6 +12,7 @@
|
|||
var gui = {
|
||||
widgets: {},
|
||||
focused_window: nil,
|
||||
open_popups: [],
|
||||
region_highlight: nil,
|
||||
|
||||
# Window/dialog stacking order
|
||||
|
@ -28,6 +29,8 @@ var loadWidget = func(name) loadGUIFile("widgets/" ~ name ~ ".nas");
|
|||
var loadDialog = func(name) loadGUIFile("dialogs/" ~ name ~ ".nas");
|
||||
|
||||
loadGUIFile("Config.nas");
|
||||
loadGUIFile("Menu.nas");
|
||||
loadGUIFile("Popup.nas");
|
||||
loadGUIFile("Style.nas");
|
||||
loadGUIFile("Widget.nas");
|
||||
loadGUIFile("styles/DefaultStyle.nas");
|
||||
|
@ -103,8 +106,10 @@ var Window = {
|
|||
m.setDouble("aspect-ratio", size[0]/size[1]);
|
||||
m.setBool("lock-aspect-ratio", 0);
|
||||
|
||||
# TODO better default position
|
||||
m.move(0,0);
|
||||
var desktopSize = [props.globals.getValue("/sim/gui/canvas/size[0]"), props.globals.getValue("/sim/gui/canvas/size[1]")];
|
||||
var pos = [desktopSize[0] / 2 - size[0] / 2 + 10, desktopSize[1] / 2 - size[1] / 2 + 30];
|
||||
m.move(pos[0], pos[1]);
|
||||
|
||||
if (destroy_on_close) {
|
||||
m.setFocus();
|
||||
} else {
|
||||
|
@ -351,6 +356,11 @@ var Window = {
|
|||
_onStateChange: func
|
||||
{
|
||||
var event = canvas.CustomEvent.new("wm.focus-" ~ (me._focused ? "in" : "out"));
|
||||
if (me._focused) {
|
||||
foreach(var p; gui.open_popups) {
|
||||
p.hide();
|
||||
}
|
||||
}
|
||||
|
||||
if( me._getCanvasDecoration() != nil )
|
||||
{
|
||||
|
@ -576,6 +586,12 @@ var Window = {
|
|||
getDesktop().addEventListener("mousedown", func {
|
||||
if( gui.focused_window != nil )
|
||||
gui.focused_window.clearFocus();
|
||||
|
||||
if (size(gui.open_popups)) {
|
||||
foreach (var p; gui.open_popups) {
|
||||
p.hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
# Provide old 'Dialog' for backwards compatiblity (should be removed for 3.0)
|
||||
|
|
268
Nasal/canvas/gui/Menu.nas
Normal file
268
Nasal/canvas/gui/Menu.nas
Normal file
|
@ -0,0 +1,268 @@
|
|||
# SPDX-FileCopyrightText: (C) 2022 Frederic Croix <thefgfseagle@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#
|
||||
# Usage example for the menu, with submenu:
|
||||
#
|
||||
# var m = canvas.gui.Menu.new();
|
||||
# var i1 = m.createItem("Text");
|
||||
# var i2 = m.createItem("Text 2");
|
||||
# var sub = canvas.gui.Menu.new();
|
||||
# var s1 = sub.createItem("Sub", nil);
|
||||
# var s2 = sub.createItem("Sub", nil);
|
||||
# i1.setMenu(sub);
|
||||
# m.setPosition(200, 200);
|
||||
# m.show();
|
||||
|
||||
gui.MenuItem = {
|
||||
# @description Create a new menu item widget
|
||||
# @cfg_field text: str Text of the new menu item
|
||||
# @cfg_field cb: callable Function / method to call when the item is clicked
|
||||
# @cfg_field icon: str Path of an icon to be displayed (relative to the path in `canvas.style._dir_widgets`)
|
||||
# @cfg_field enabled: bool Initial state of the menu item: enabled (1) or disabled (0)
|
||||
new: func(parent, style, cfg) {
|
||||
var cfg = Config.new(cfg);
|
||||
var m = gui.Widget.new(gui.MenuItem);
|
||||
m._text = cfg.get("text", "Menu item");
|
||||
m._cb = cfg.get("cb", nil);
|
||||
m._icon = cfg.get("icon", nil);
|
||||
m._enabled = cfg.get("enabled", 1);
|
||||
m._hovered = 0;
|
||||
m._menu = nil;
|
||||
m._parent_menu = nil;
|
||||
|
||||
m._setView(style.createWidget(parent, cfg.get("type", "menu-item"), cfg));
|
||||
|
||||
m.setLayoutMinimumSize([48, 24]);
|
||||
m.setLayoutSizeHint([64, 24]);
|
||||
m.setLayoutMaximumSize([1024, 24]);
|
||||
m._view.setText(m, m._text);
|
||||
m._view.setIcon(m._icon);
|
||||
return m;
|
||||
},
|
||||
|
||||
setMenu: func(menu) {
|
||||
me._menu = menu;
|
||||
|
||||
return me.update();
|
||||
},
|
||||
|
||||
onClicked: func(e) {
|
||||
if (!me._menu and me._cb) {
|
||||
me._cb(e);
|
||||
}
|
||||
},
|
||||
|
||||
onMouseEnter: func(e) {
|
||||
print("entered item ", me._enabled);
|
||||
for (var i = 0; i < me._parent_menu._layout.count(); i += 1) {
|
||||
var item = me._parent_menu._layout.itemAt(i);
|
||||
item._hovered = 0;
|
||||
if (item._menu) {
|
||||
item._menu.hide();
|
||||
}
|
||||
item.update();
|
||||
}
|
||||
if (me._enabled) {
|
||||
me._hovered = 1;
|
||||
var x = e.screenX - e.localX + me.geometry()[2];
|
||||
var y = e.screenY - e.localY;
|
||||
me._showMenu(x, y);
|
||||
}
|
||||
me.update();
|
||||
},
|
||||
|
||||
onMouseLeave: func(e) {
|
||||
print("left item");
|
||||
if (me._menu == nil) {
|
||||
me._hovered = 0;
|
||||
}
|
||||
me.update();
|
||||
},
|
||||
|
||||
removeMenu: func() {
|
||||
me._menu = nil;
|
||||
return me.update();
|
||||
},
|
||||
|
||||
_showMenu: func(x, y) {
|
||||
if (me._menu) {
|
||||
me._menu.setPosition(x, y);
|
||||
me._menu.show();
|
||||
me._menu.setFocus();
|
||||
}
|
||||
},
|
||||
|
||||
setEnabled: func(enabled = 1) {
|
||||
me._enabled = enabled;
|
||||
return me.update();
|
||||
},
|
||||
|
||||
setText: func(text) {
|
||||
me._text = text;
|
||||
me._view.setText(me, text);
|
||||
return me.update();
|
||||
},
|
||||
|
||||
setIcon: func(icon) {
|
||||
me._icon = icon;
|
||||
me._view.setIcon(icon);
|
||||
return me.update();
|
||||
},
|
||||
|
||||
setCallback: func(cb = nil) {
|
||||
me._cb = cb;
|
||||
return me;
|
||||
},
|
||||
|
||||
update: func {
|
||||
me._view.update(me);
|
||||
return me;
|
||||
},
|
||||
|
||||
_setView: func(view) {
|
||||
call(gui.Widget._setView, [view], me);
|
||||
|
||||
var el = view._root;
|
||||
el.addEventListener("click", func(e) me.onClicked(e));
|
||||
|
||||
el.addEventListener("mouseenter", func(e) me.onMouseEnter(e));
|
||||
el.addEventListener("mouseleave", func(e) me.onMouseLeave(e));
|
||||
el.addEventListener("drag", func(e) e.stopPropagation());
|
||||
}
|
||||
};
|
||||
|
||||
gui.Menu = {
|
||||
new: func(id = nil) {
|
||||
var m = gui.Popup.new([100, 60], id);
|
||||
m.parents = [gui.Menu] ~ m.parents;
|
||||
m.style = style;
|
||||
|
||||
m._canvas = m.createCanvas().setColorBackground(style.getColor("bg_color"));
|
||||
m._root = m._canvas.createGroup();
|
||||
m._layout = VBoxLayout.new();
|
||||
m._layout.setSpacing(0);
|
||||
m.setLayout(m._layout);
|
||||
m.hide();
|
||||
|
||||
return m;
|
||||
},
|
||||
|
||||
# @description Add the given menu item to the menu (normally a `canvas.gui.MenuItem`, but can be any `canvas.gui.Widget` in theory)
|
||||
# @return canvas.gui.Menu Return me to enable method chaining
|
||||
addItem: func(item) {
|
||||
item._parent_menu = me;
|
||||
me._layout.addItem(item);
|
||||
me.setSize(me._layout.minimumSize()[0], me._layout.minimumSize()[1]);
|
||||
return me;
|
||||
},
|
||||
|
||||
# @description Create, insert and return a `canvas.gui.MenuItem` with given text and an optional callback, icon and enabled state.
|
||||
# @param text: strrequired Text to display on the menu item
|
||||
# @param cb: callable optional Function / method to call when the item is clicked - if no callback is wanted, nil can be used
|
||||
# @param icon: str optional Path to the icon (relative to canvas.style._dir_widgets) or nil if none should be displayed
|
||||
# @param enabled: bool optional Whether the item should be enabled (1) or disabled (0)
|
||||
# @return canvas.gui.MenuItem The item that was created
|
||||
createItem: func(text, cb = nil, icon = nil, enabled = 1) {
|
||||
item = gui.MenuItem.new(me._root, me.style, {"text": text, "cb": cb, "icon": icon, "enabled": enabled});
|
||||
me.addItem(item);
|
||||
return item;
|
||||
},
|
||||
|
||||
# @description Create, insert and return a `canvas.gui.MenuItem with the given text and assign the given submenu to it,
|
||||
# optionally add the given icon and set the given enabled state
|
||||
# @param text: strrequired Text to display on the menu item
|
||||
# @param menu: canvas.gui.Menu Submenu that shall be assigned to the new menu item
|
||||
# @param icon: str optional Path to the icon (relative to canvas.style._dir_widgets) or nil if none should be displayed
|
||||
# @param enabled: bool optional Whether the item should be enabled (1) or disabled (0)
|
||||
# @return canvas.gui.MenuItem The item that was created
|
||||
addMenu: func(text, menu, icon = nil, enabled = 1) {
|
||||
item = gui.MenuItem.new(me._root, me.style, {"text": text, cb: nil, "icon": icon, "enabled": enabled});
|
||||
item.setMenu(menu);
|
||||
me.addItem(item);
|
||||
return item;
|
||||
},
|
||||
|
||||
# @description Remove all items from the menu
|
||||
# @return canvas.gui.Menu Return me to enable method chaining
|
||||
clear: func {
|
||||
me._layout.clear();
|
||||
return me;
|
||||
},
|
||||
|
||||
# @description If `item` is a `canvas.gui.Widget`, remove the given `canvas.gui.Widget` from the menu
|
||||
# Else assume `item` to be a scalar and try to find an item of the menu that has a `getText` method
|
||||
# and whose result of calling its `getText` method equals `item` and remove that item
|
||||
# @param item: Union[str, canvas.gui.Widget] required The widget or the text of the menu item to remove
|
||||
removeItem: func(item) {
|
||||
if (isa(item, gui.Widget)) {
|
||||
me._layout.removeItem(item);
|
||||
} else {
|
||||
for (var i = 0; i < me._layout.count(); i += 1) {
|
||||
if (me._layout.itemAt(i)["getText"] != nil and me._layout.itemAt(i).getText() == item) {
|
||||
me._layout.takeAt(i);
|
||||
return me;
|
||||
}
|
||||
}
|
||||
die("No menu item with given text '" ~ item ~ "' found, could not remove !");
|
||||
}
|
||||
},
|
||||
|
||||
# @description If `index` is an integer, remove and return the item at the given `index`
|
||||
# Else assume `item` to be a scalar and try to find an item of the menu that has a `getText` method
|
||||
# and whose result of calling its `getText` method equals `item` and remove and return that item
|
||||
# @param index: Union[int, str] required The index or text of the menu item to remove
|
||||
# @return canvas.gui.Widget The item with given text `index` or at the given position `index`
|
||||
takeAt: func(index) {
|
||||
if (isint(index)) {
|
||||
return me._layout.takeAt(index);
|
||||
} else {
|
||||
for (var i = 0; i < me._layout.count(); i += 1) {
|
||||
if (me._layout.itemAt(i)["getText"] != nil and me._layout.itemAt(i).getText() == index) {
|
||||
return me._layout.takeAt(i);
|
||||
}
|
||||
}
|
||||
die("No menu item with given text '" ~ index ~ "' found, could not remove !");
|
||||
}
|
||||
},
|
||||
|
||||
# @description Count the items of the menu
|
||||
# @return int Number of items
|
||||
count: func() {
|
||||
return me._layout.count();
|
||||
},
|
||||
|
||||
# @description If `index` is an integer, eturn the item at the given `index`
|
||||
# Else assume `item` to be a scalar and try to find an item of the menu that has a `getText` method
|
||||
# and whose result of calling its `getText` method equals `item` and eturn that item
|
||||
# @param index: Union[int, str] required The index or text of the menu item to return
|
||||
# @return canvas.gui.Widget The item with given text `index` or at the given position `index`
|
||||
getItem: func(index) {
|
||||
if (isint(index)) {
|
||||
return me._layout.itemAt(index);
|
||||
} else {
|
||||
for (var i = 0; i < me._layout.count(); i += 1) {
|
||||
if (me._layout.itemAt(i)["getText"] != nil and me._layout.itemAt(i).getText() == index) {
|
||||
return me._layout.itemAt(i);
|
||||
}
|
||||
}
|
||||
die("No menu item with given text '" ~ index ~ "' found, could not remove !");
|
||||
}
|
||||
},
|
||||
|
||||
# @description Destructor
|
||||
del: func() {
|
||||
me.hide();
|
||||
me.clear();
|
||||
me._canvas.del();
|
||||
},
|
||||
|
||||
# @description Update the menu and its items
|
||||
update: func() {
|
||||
me.parents[1].update();
|
||||
for (var i = 0; i < me._layout.count(); i += 1) {
|
||||
me._layout.itemAt(i).update();
|
||||
}
|
||||
return me;
|
||||
},
|
||||
};
|
||||
|
285
Nasal/canvas/gui/Popup.nas
Normal file
285
Nasal/canvas/gui/Popup.nas
Normal file
|
@ -0,0 +1,285 @@
|
|||
# SPDX-FileCopyrightText: (C) 2022 Frederic Croix <thefgfseagle@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
gui.Popup = {
|
||||
__used_ids: [],
|
||||
# Constructor
|
||||
#
|
||||
# @param size ([width, height])
|
||||
new: func(size_, id = nil, parent = nil) {
|
||||
if (id == nil or contains(gui.Popup.__used_ids, id)) {
|
||||
id = "popup" ~ size(gui.Popup.__used_ids);
|
||||
}
|
||||
append(gui.Popup.__used_ids, id);
|
||||
var ghost = _newWindowGhost(id);
|
||||
var m = {
|
||||
parents: [gui.Popup, PropertyElement, ghost],
|
||||
_ghost: ghost,
|
||||
_node: props.wrapNode(ghost._node_ghost),
|
||||
_focused: 0,
|
||||
_widgets: [],
|
||||
_parent: parent,
|
||||
_canvas: nil,
|
||||
};
|
||||
|
||||
m.setInt("content-size[0]", size_[0]);
|
||||
m.setInt("content-size[1]", size_[1]);
|
||||
|
||||
m.setFocus();
|
||||
|
||||
# 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 {
|
||||
me.clearFocus();
|
||||
|
||||
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;
|
||||
},
|
||||
#
|
||||
setFocus: func {
|
||||
if (gui.focused_window != nil) {
|
||||
gui.focused_window.clearFocus();
|
||||
}
|
||||
|
||||
# me.onFocusIn();
|
||||
me._focused = 1;
|
||||
me._onStateChange();
|
||||
gui.focused_window = me;
|
||||
setInputFocus(me);
|
||||
return me;
|
||||
},
|
||||
#
|
||||
clearFocus: func {
|
||||
# me.onFocusOut();
|
||||
me._focused = 0;
|
||||
me._onStateChange();
|
||||
if (gui.focused_window == me) {
|
||||
gui.focused_window = nil;
|
||||
setInputFocus(nil);
|
||||
}
|
||||
if (me._parent != nil and contains(gui.open_popups, me._parent)) {
|
||||
me._parent.setFocus();
|
||||
}
|
||||
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.onResize != nil) {
|
||||
me.onResize();
|
||||
}
|
||||
|
||||
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"]));
|
||||
|
||||
me.setFocus();
|
||||
},
|
||||
hide: func(parents = 0) {
|
||||
me.clearFocus();
|
||||
me._ghost.hide();
|
||||
for (var i = 0; i < size(gui.open_popups); i += 1) {
|
||||
if (gui.open_popups[i] == me) {
|
||||
gui.open_popups = subvec(gui.open_popups, 0, i) ~ subvec(gui.open_popups, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (me._parent != nil) {
|
||||
me._parent.setFocus();
|
||||
}
|
||||
},
|
||||
show: func() {
|
||||
me._ghost.show();
|
||||
me.raise();
|
||||
if (me._canvas != nil) {
|
||||
me._canvas.update();
|
||||
}
|
||||
for (var i = 0; i < size(gui.open_popups); i += 1) {
|
||||
gui.open_popups[i].clearFocus();
|
||||
}
|
||||
append(gui.open_popups, me);
|
||||
},
|
||||
# Hide / show the window based on whether it's currently visible
|
||||
toggle: func() {
|
||||
if (me.isVisible()) {
|
||||
me.hide();
|
||||
} else {
|
||||
me.show();
|
||||
me.raise();
|
||||
}
|
||||
},
|
||||
onResize: func() {
|
||||
if (me['_canvas'] == 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);
|
||||
}
|
||||
},
|
||||
# protected:
|
||||
_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);
|
||||
}
|
||||
},
|
||||
# private:
|
||||
#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);
|
||||
}
|
||||
},
|
||||
_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 ~ "]")
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -853,4 +853,69 @@ DefaultStyle.widgets.slider = {
|
|||
}
|
||||
return me;
|
||||
}
|
||||
};
|
||||
|
||||
DefaultStyle.widgets["menu-item"] = {
|
||||
new: func(parent, cfg) {
|
||||
me._root = parent.createChild("group", "menu-item");
|
||||
me._bg = me._root.createChild("path");
|
||||
|
||||
me._icon = me._root.createChild("image");
|
||||
|
||||
me._label = me._root.createChild("text")
|
||||
.set("font", "LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.set("character-size", 14)
|
||||
.set("alignment", "left-center");
|
||||
|
||||
me._submenu_indicator = me._root.createChild("path")
|
||||
.vert(12).line(6, -7).close();
|
||||
},
|
||||
|
||||
setSize: func(model, w, h) {
|
||||
me._bg.reset().rect(0, 0, w, h);
|
||||
me._icon.setTranslation(3, int((h - 12) / 2));
|
||||
me._label.setTranslation(24, int(h / 2) + 1);
|
||||
me._submenu_indicator.setTranslation(w - 15, int((h - 12) / 2));
|
||||
return me;
|
||||
},
|
||||
|
||||
setText: func(model, text) {
|
||||
me._label.setText(text);
|
||||
|
||||
var min_width = me._label.maxWidth() + 6 + 48;
|
||||
model.setLayoutMinimumSize([min_width, 24]);
|
||||
model.setLayoutSizeHint([min_width, 24]);
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
setIcon: func(icon) {
|
||||
if (!icon) {
|
||||
me._icon.hide();
|
||||
} else {
|
||||
me._icon.show();
|
||||
var file = me._style._dir_widgets ~ "/" ~ icon;
|
||||
me._icon.set("src", file);
|
||||
}
|
||||
return me;
|
||||
},
|
||||
|
||||
update: func(model) {
|
||||
me._bg.set("fill", me._style.getColor("menu_item_bg" ~ (model._hovered ? "_hovered" : "")));
|
||||
var text_color_name = "menu_item_fg";
|
||||
if (model._hovered) {
|
||||
text_color_name ~= "_hovered";
|
||||
} else if (!model._enabled) {
|
||||
text_color_name ~= "_disabled";
|
||||
}
|
||||
me._label.set("fill", me._style.getColor(text_color_name));
|
||||
me._submenu_indicator.set("fill", me._style.getColor("menu_item_submenu_indicator" ~ (model._hovered ? "_hovered" : "")));
|
||||
if (model._menu != nil) {
|
||||
me._submenu_indicator.show();
|
||||
} else {
|
||||
me._submenu_indicator.hide();
|
||||
}
|
||||
|
||||
return me;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# SPDX-FileCopyrightText: (C) 2022 James Turner <james@flightgear.org>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
gui.widgets.Menu = {
|
||||
|
||||
new: func(parent, style, cfg)
|
||||
{
|
||||
var cfg = Config.new(cfg);
|
||||
var m = gui.Widget.new(gui.widgets.Menu);
|
||||
m._focus_policy = m.StrongFocus;
|
||||
|
||||
# m._flat = cfg.get("flat", 0);
|
||||
# m._isDefault = cfg.get("default", 0);
|
||||
# m._destructive = cfg.get("destructive", 0);
|
||||
|
||||
m._setView( style.createWidget(parent, cfg.get("type", "menu"), cfg) );
|
||||
return m;
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
# protected:
|
||||
_setView: func(view)
|
||||
{
|
||||
call(gui.Widget._setView, [view], me);
|
||||
var el = view._root;
|
||||
}
|
||||
};
|
||||
|
|
@ -137,5 +137,47 @@
|
|||
<green type="float">0.955</green>
|
||||
<blue type="float">0.95</blue>
|
||||
</tab_widget_tab_button_bg_selected>
|
||||
|
||||
<menu_item_fg>
|
||||
<red type="float">0.298</red>
|
||||
<green type="float">0.298</green>
|
||||
<blue type="float">0.298</blue>
|
||||
</menu_item_fg>
|
||||
|
||||
<menu_item_fg_disabled>
|
||||
<red type="float">0.89</red>
|
||||
<green type="float">0.89</green>
|
||||
<blue type="float">0.89</blue>
|
||||
</menu_item_fg_disabled>
|
||||
|
||||
<menu_item_fg_hovered>
|
||||
<red type="float">0.95</red>
|
||||
<green type="float">0.95</green>
|
||||
<blue type="float">0.95</blue>
|
||||
</menu_item_fg_hovered>
|
||||
|
||||
<menu_item_bg>
|
||||
<red type="float">0.949</red>
|
||||
<green type="float">0.945</green>
|
||||
<blue type="float">0.941</blue>
|
||||
</menu_item_bg>
|
||||
|
||||
<menu_item_bg_hovered>
|
||||
<red type="float">0.15</red>
|
||||
<green type="float">0.15</green>
|
||||
<blue type="float">1</blue>
|
||||
</menu_item_bg_hovered>
|
||||
|
||||
<menu_item_submenu_indicator>
|
||||
<red type="float">0</red>
|
||||
<green type="float">0</green>
|
||||
<blue type="float">0</blue>
|
||||
</menu_item_submenu_indicator>
|
||||
|
||||
<menu_item_submenu_indicator_hovered>
|
||||
<red type="float">0.949</red>
|
||||
<green type="float">0.945</green>
|
||||
<blue type="float">0.941</blue>
|
||||
</menu_item_submenu_indicator_hovered>
|
||||
</colors>
|
||||
</PropertyList>
|
||||
|
|
Loading…
Add table
Reference in a new issue