1
0
Fork 0

Added basic tab widget

This commit is contained in:
TheFGFSEagle 2022-12-20 17:41:39 +01:00 committed by James Turner
parent 09fa5499bb
commit 8d5e4aaf1c
5 changed files with 299 additions and 6 deletions

View file

@ -186,6 +186,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 {
var bb = me.getTightBoundingBox();

View file

@ -40,6 +40,7 @@ loadWidget("LineEdit");
loadWidget("ScrollArea");
loadWidget("Rule");
loadWidget("Slider");
loadWidget("TabWidget");
# standard dialogs
loadDialog("InputDialog");

View file

@ -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 = {

View 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;
},
};

View file

@ -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>
@ -96,6 +94,40 @@
<blue type="float">0.89</blue>
</button_bg_color_insensitive>
</colors>
<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>