Added basic tab widget
This commit is contained in:
parent
09fa5499bb
commit
8d5e4aaf1c
5 changed files with 299 additions and 6 deletions
|
@ -185,6 +185,22 @@ var Element = {
|
|||
}
|
||||
return center;
|
||||
},
|
||||
|
||||
# Set size in pixels
|
||||
# Use either with x, y size:
|
||||
# e.setSize(<x>, <y>)
|
||||
# or with a vector containing x, y:
|
||||
# e.setSize([<x>, <y>])
|
||||
setSize: func {
|
||||
if (size(arg) == 1) {
|
||||
var (x, y) = arg[0];
|
||||
} else {
|
||||
var (x, y) = arg;
|
||||
}
|
||||
var (sx, sy) = me.getScale();
|
||||
var (curx, cury) = me.getSize();
|
||||
me.setScale(x / (curx / sy), y / (cury / sy));
|
||||
},
|
||||
|
||||
#return vector [sx, sy] with dimensions of bounding box
|
||||
getSize: func {
|
||||
|
|
|
@ -40,6 +40,7 @@ loadWidget("LineEdit");
|
|||
loadWidget("ScrollArea");
|
||||
loadWidget("Rule");
|
||||
loadWidget("Slider");
|
||||
loadWidget("TabWidget");
|
||||
|
||||
# standard dialogs
|
||||
loadDialog("InputDialog");
|
||||
|
|
|
@ -126,7 +126,7 @@ DefaultStyle.widgets.checkbox = {
|
|||
{
|
||||
if (me._label_position == "left") {
|
||||
me._label.setTranslation(3, int((h / 2) + 1));
|
||||
me._icon.setTranslation(me._label.maxWidth() + 6, int((h - 18) / 2));
|
||||
me._icon.setTranslation(w - 24, int((h - 18) / 2));
|
||||
} else {
|
||||
me._icon.setTranslation(3, int((h - 18) / 2));
|
||||
me._label.setTranslation(24, int(h / 2) + 1);
|
||||
|
@ -458,6 +458,88 @@ DefaultStyle.widgets["scroll-area"] = {
|
|||
}
|
||||
};
|
||||
|
||||
DefaultStyle.widgets["tab-widget"] = {
|
||||
new: func(parent, cfg) {
|
||||
me._root = parent.createChild("group", "tab-widget");
|
||||
me._bg = me._root.createChild("path", "background")
|
||||
.set("fill", "#e0e0e0");
|
||||
me.tabBar = me._root.createChild("group", "tab-widget-tabbar");
|
||||
me.tabBarHeight = 30;
|
||||
me.content = me._root.createChild("group", "tab-widget-content");
|
||||
me.content.setTranslation(0, me.tabBarHeight);
|
||||
},
|
||||
|
||||
update: func(model) {
|
||||
me._bg.set("fill", me._style.getColor("bg_color"));
|
||||
},
|
||||
|
||||
setSize: func(model, w, h) {
|
||||
me._bg.reset().rect(0, 0, model._size[0], model._size[1]);
|
||||
me.tabBar.set("clip", sprintf("rect(0, %d, %d, 0)", model._size[0], me.tabBarHeight));
|
||||
me.content.set("clip", sprintf("rect(0, %d, %d, 0)", model._size[0], model._size[1] - me.tabBarHeight));
|
||||
|
||||
return me.update(model);
|
||||
},
|
||||
};
|
||||
|
||||
# Tab button for the tab widget
|
||||
DefaultStyle.widgets["tab-widget-tab-button"] = {
|
||||
new: func(parent, cfg) {
|
||||
me._root = parent.createChild("group", "tab-widget-tab-button");
|
||||
me._bg = me._root.createChild("path")
|
||||
.set("fill", me._style.getColor("tab_widget_tab_button_bg_focused"))
|
||||
.set("stroke", me._style.getColor("tab_widget_tab_button_border"))
|
||||
.set("stroke-width", 1);
|
||||
me._selected_indicator = me._root.createChild("path")
|
||||
.set("stroke", me._style.getColor("tab_widget_tab_button_selected_indicator"))
|
||||
.set("stroke-width", 3);
|
||||
me._label = me._root.createChild("text")
|
||||
.set("font", "LiberationFonts/LiberationSans-Regular.ttf")
|
||||
.set("character-size", 14)
|
||||
.set("alignment", "center-baseline");
|
||||
},
|
||||
|
||||
setSize: func(model, w, h) {
|
||||
me._bg.reset().rect(3, 0, w - 6, h);
|
||||
me._selected_indicator.reset().moveTo(3, h).horiz(w - 6);
|
||||
},
|
||||
|
||||
setText: func(model, text) {
|
||||
me._label.setText(text);
|
||||
|
||||
var min_width = math.max(80, me._label.maxWidth() + 16);
|
||||
model.setLayoutMinimumSize([min_width, 30]);
|
||||
model.setLayoutSizeHint([min_width, 30]);
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
update: func(model) {
|
||||
var backdrop = !model._windowFocus();
|
||||
var (w, h) = model._size;
|
||||
|
||||
me._label.setTranslation(w / 2, h / 2 + 5);
|
||||
|
||||
var bg_color_name = "tab_widget_tab_button_bg_focused";
|
||||
if (backdrop) {
|
||||
bg_color_name = "tab_widget_tab_button_bg_unfocused";
|
||||
} else if (model._selected) {
|
||||
bg_color_name = "tab_widget_tab_button_bg_selected";
|
||||
} else if (model._hover) {
|
||||
bg_color_name = "tab_widget_tab_button_bg_hovered";
|
||||
}
|
||||
me._bg.set("fill", me._style.getColor(bg_color_name));
|
||||
|
||||
me._selected_indicator.setVisible(model._selected);
|
||||
|
||||
if (backdrop) {
|
||||
me._label.set("fill", me._style.getColor("backdrop_fg_color"));
|
||||
} else {
|
||||
me._label.set("fill", me._style.getColor("fg_color"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
# A horizontal or vertical rule line
|
||||
# possibly with a text label embedded
|
||||
DefaultStyle.widgets.rule = {
|
||||
|
|
162
Nasal/canvas/gui/widgets/TabWidget.nas
Normal file
162
Nasal/canvas/gui/widgets/TabWidget.nas
Normal file
|
@ -0,0 +1,162 @@
|
|||
# TabWidget.nas - widget with a tabs bar and a content area which always displays
|
||||
# exactly one of its tabs
|
||||
|
||||
# SPDX-FileCopyrightText: (C) 2022 Frederic Croix <thefgfseagle@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
|
||||
gui.widgets.TabWidgetTabButton = {
|
||||
new: func(parent, style, cfg) {
|
||||
var cfg = Config.new(cfg);
|
||||
var m = gui.Widget.new(gui.widgets.TabWidgetTabButton);
|
||||
m._focus_policy = m.StrongFocus;
|
||||
m._selected = 0;
|
||||
|
||||
m._setView(style.createWidget(parent, "tab-widget-tab-button", cfg));
|
||||
|
||||
return m;
|
||||
},
|
||||
setText: func(text) {
|
||||
me._view.setText(me, text);
|
||||
|
||||
return me;
|
||||
},
|
||||
setSelected: func(selected = 1) {
|
||||
if (me._selected == selected) {
|
||||
return me;
|
||||
}
|
||||
|
||||
me._selected = selected;
|
||||
me._trigger("toggled", {selected: selected});
|
||||
me._onStateChange();
|
||||
me._view.update(me);
|
||||
return me;
|
||||
},
|
||||
_setView: func(view) {
|
||||
call(gui.Widget._setView, [view], me);
|
||||
|
||||
var el = view._root;
|
||||
#el.addEventListener("mousedown", func me.dragStart());
|
||||
#el.addEventListener("mouseup", func me.dragStop());
|
||||
el.addEventListener("click", func me.setSelected());
|
||||
|
||||
el.addEventListener("drag", func(e) { e.stopPropagation() });
|
||||
#el.addEventListener("drag", me.drag);
|
||||
}
|
||||
};
|
||||
|
||||
gui.widgets.TabWidget = {
|
||||
new: func(parent, style, cfg) {
|
||||
var m = gui.Widget.new(gui.widgets.TabWidget);
|
||||
m._cfg = Config.new(cfg);
|
||||
m._focus_policy = m.NoFocus;
|
||||
m._setView(style.createWidget(parent, "tab-widget", m._cfg));
|
||||
m._layout = VBoxLayout.new();
|
||||
m._layout.setCanvas(m._view._root.getCanvas());
|
||||
m._tabBar = HBoxLayout.new();
|
||||
m._layout.addItem(m._tabBar);
|
||||
m._currentTab = nil;
|
||||
m._currentTabId = nil;
|
||||
m._tabs = {};
|
||||
m._tabButtons = {};
|
||||
|
||||
m.setLayoutMinimumSize([50, 30]);
|
||||
m.setLayoutSizeHint([100, 30]);
|
||||
|
||||
return m;
|
||||
},
|
||||
|
||||
hasTab: func(id) {
|
||||
return me._tabs[id] != nil;
|
||||
},
|
||||
|
||||
getTab: func(id) {
|
||||
if (!me.hasTab(id)) {
|
||||
die("tab with id '" ~ id ~ "' does not exist");
|
||||
}
|
||||
|
||||
return me._tabs[id];
|
||||
},
|
||||
|
||||
addTab: func(id, label, widget) {
|
||||
if (me.hasTab(id)) {
|
||||
die("cannot add multiple tabs with the same id: " ~ id);
|
||||
}
|
||||
|
||||
me._tabButtons[id] = gui.widgets.TabWidgetTabButton.new(me._view._root, canvas.style, {})
|
||||
.setText(label)
|
||||
.listen("toggled", func (e) {
|
||||
if (e.detail.selected and id != me._currentTabId) {
|
||||
me.setCurrentTab(id);
|
||||
}
|
||||
});
|
||||
|
||||
me._tabBar.addItem(me._tabButtons[id]);
|
||||
me._tabs[id] = widget;
|
||||
me._layout.addItem(widget);
|
||||
me.setCurrentTab(keys(me._tabs)[0]);
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
removeTab: func(id) {
|
||||
if (!me.hasTab(id)) {
|
||||
die("tab with id '" ~ id ~ "' does not exist");
|
||||
}
|
||||
|
||||
me._tabs[id].setVisible(0);
|
||||
me._layout.removeItem(me._tabs[id]);
|
||||
delete(me._tabs, id);
|
||||
me._tabBar.removeItem(me._tabButtons[id]);
|
||||
delete(me._tabButtons, id);
|
||||
if (size(keys(me._tabs)) > 0) {
|
||||
me.setCurrentTab(keys(me._tabs)[-1]);
|
||||
}
|
||||
|
||||
return me;
|
||||
},
|
||||
|
||||
setCurrentTab: func(id) {
|
||||
if (!me.hasTab(id)) {
|
||||
die("tab with id '" ~ id ~ "' does not exist");
|
||||
}
|
||||
|
||||
if (me._currentTabId) {
|
||||
me._tabButtons[me._currentTabId].setSelected(0);
|
||||
}
|
||||
me._tabButtons[id].setSelected();
|
||||
foreach (var tabid; keys(me._tabs)) {
|
||||
me._tabs[tabid].setVisible(0);
|
||||
}
|
||||
me._tabs[id].setVisible(1);
|
||||
me._currentTabId = id;
|
||||
me._currentTab = me._tabs[id];
|
||||
|
||||
return me.update();
|
||||
},
|
||||
|
||||
setSize: func {
|
||||
if (size(arg) == 1) {
|
||||
var arg = arg[0];
|
||||
}
|
||||
var (x, y) = arg;
|
||||
me._size = [x, y];
|
||||
return me.update();
|
||||
},
|
||||
|
||||
# Needs to be called when the size of the content changes.
|
||||
update: func() {
|
||||
if(me._layout.getParent() == nil) {
|
||||
me._layout.setParent(me);
|
||||
}
|
||||
|
||||
me._tabBar.setGeometry([0, 0, me._size[0], me._view.tabBarHeight]);
|
||||
if (me._currentTab) {
|
||||
me._currentTab.setGeometry([0, me._view.tabBarHeight, me._size[0], me._size[1] - me._view.tabBarHeight]);
|
||||
}
|
||||
|
||||
me._view.update(me);
|
||||
|
||||
return me;
|
||||
},
|
||||
};
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PropertyList>
|
||||
<!--
|
||||
<folders>
|
||||
|
@ -8,7 +7,6 @@
|
|||
</folders>
|
||||
-->
|
||||
<colors>
|
||||
|
||||
<!-- Window decoration colors -->
|
||||
<title>
|
||||
<red type="float">0.275</red>
|
||||
|
@ -95,7 +93,41 @@
|
|||
<green type="float">0.89</green>
|
||||
<blue type="float">0.89</blue>
|
||||
</button_bg_color_insensitive>
|
||||
|
||||
|
||||
<tab_widget_tab_button_border>
|
||||
<red type="float">0.905</red>
|
||||
<green type="float">0.897</green>
|
||||
<blue type="float">0.885</blue>
|
||||
</tab_widget_tab_button_border>
|
||||
|
||||
<tab_widget_tab_button_selected_indicator>
|
||||
<red type="float">0.11</red>
|
||||
<green type="float">0.42</green>
|
||||
<blue type="float">0.8</blue>
|
||||
</tab_widget_tab_button_selected_indicator>
|
||||
|
||||
<tab_widget_tab_button_bg_focused>
|
||||
<red type="float">0.86</red>
|
||||
<green type="float">0.855</green>
|
||||
<blue type="float">0.85</blue>
|
||||
</tab_widget_tab_button_bg_focused>
|
||||
|
||||
<tab_widget_tab_button_bg_unfocused>
|
||||
<red type="float">0.9</red>
|
||||
<green type="float">0.895</green>
|
||||
<blue type="float">0.89</blue>
|
||||
</tab_widget_tab_button_bg_unfocused>
|
||||
|
||||
<tab_widget_tab_button_bg_hovered>
|
||||
<red type="float">0.93</red>
|
||||
<green type="float">0.925</green>
|
||||
<blue type="float">0.92</blue>
|
||||
</tab_widget_tab_button_bg_hovered>
|
||||
|
||||
<tab_widget_tab_button_bg_selected>
|
||||
<red type="float">0.96</red>
|
||||
<green type="float">0.955</green>
|
||||
<blue type="float">0.95</blue>
|
||||
</tab_widget_tab_button_bg_selected>
|
||||
</colors>
|
||||
|
||||
</PropertyList>
|
||||
|
|
Loading…
Add table
Reference in a new issue