Add a ScrollBar to the VerticalList.

This commit is contained in:
IntegratedQuantum 2023-03-21 11:45:29 +01:00
parent ef0f9ebf67
commit 219c3214e9
10 changed files with 121 additions and 61 deletions

View File

@ -76,6 +76,7 @@ pub fn updateHovered(self: *TextInput, mousePosition: Vec2f) void {
if(self.textSize[1] > self.maxHeight - 2*border) {
const diff = self.textSize[1] - (self.maxHeight - 2*border);
self.scrollBar.scroll(-main.Window.scrollOffset*32/diff);
main.Window.scrollOffset = 0;
}
if(self.textSize[1] > self.maxHeight - 2*border) {
self.scrollBar.pos = Vec2f{self.size[0] - border - scrollBarWidth, border};

View File

@ -10,19 +10,32 @@ const Vec2f = vec.Vec2f;
const gui = @import("../gui.zig");
const GuiComponent = gui.GuiComponent;
const ScrollBar = GuiComponent.ScrollBar;
const VerticalList = @This();
const scrollBarWidth = 5;
const border: f32 = 3;
pos: Vec2f,
size: Vec2f,
children: std.ArrayList(GuiComponent),
padding: f32,
maxHeight: f32,
childrenHeight: f32 = 0,
scrollBar: *ScrollBar,
scrollBarEnabled: bool = false,
pub fn init() Allocator.Error!*VerticalList {
pub fn init(pos: Vec2f, maxHeight: f32, padding: f32) Allocator.Error!*VerticalList {
const scrollBar = try ScrollBar.init(undefined, scrollBarWidth, maxHeight - 2*border, 0);
const self = try gui.allocator.create(VerticalList);
self.* = VerticalList {
.children = std.ArrayList(GuiComponent).init(gui.allocator),
.pos = undefined,
.pos = pos,
.size = .{0, 0},
.padding = padding,
.maxHeight = maxHeight,
.scrollBar = scrollBar,
};
return self;
}
@ -31,6 +44,7 @@ pub fn deinit(self: *const VerticalList) void {
for(self.children.items) |*child| {
child.deinit();
}
self.scrollBar.deinit();
self.children.deinit();
gui.allocator.destroy(self);
}
@ -51,12 +65,12 @@ pub fn add(self: *VerticalList, _other: anytype) Allocator.Error!void {
const added = try self.children.addOne();
added.* = other;
added.mutPos().*[1] += self.size[1];
if(self.size[1] != 0) added.mutPos().*[1] += self.padding;
self.size[1] = added.pos()[1] + added.size()[1];
self.size[0] = @max(self.size[0], added.pos()[0] + added.size()[0]);
}
pub fn finish(self: *VerticalList, pos: Vec2f, alignment: graphics.TextBuffer.Alignment) void {
self.pos = pos;
pub fn finish(self: *VerticalList, alignment: graphics.TextBuffer.Alignment) void {
self.children.shrinkAndFree(self.children.items.len);
for(self.children.items) |_child| {
const child: GuiComponent = _child;
@ -72,6 +86,13 @@ pub fn finish(self: *VerticalList, pos: Vec2f, alignment: graphics.TextBuffer.Al
},
}
}
if(self.size[1] > self.maxHeight) {
self.scrollBarEnabled = true;
self.childrenHeight = self.size[1];
self.size[1] = self.maxHeight;
self.scrollBar.pos = .{self.size[0] + border, border};
self.size[0] += 2*border + scrollBarWidth;
}
}
pub fn updateSelected(self: *VerticalList) void {
@ -81,39 +102,77 @@ pub fn updateSelected(self: *VerticalList) void {
}
pub fn updateHovered(self: *VerticalList, mousePosition: Vec2f) void {
var shiftedPos = self.pos;
if(self.scrollBarEnabled) {
const diff = self.childrenHeight - self.maxHeight;
shiftedPos[1] -= diff*self.scrollBar.currentState;
}
var i: usize = self.children.items.len;
while(i != 0) {
i -= 1;
const child = &self.children.items[i];
if(GuiComponent.contains(child.pos() + self.pos, child.size(), mousePosition)) {
child.updateHovered(mousePosition - self.pos);
if(GuiComponent.contains(child.pos() + shiftedPos, child.size(), mousePosition)) {
child.updateHovered(mousePosition - shiftedPos);
break;
}
}
if(self.scrollBarEnabled) {
const diff = self.childrenHeight - self.maxHeight;
self.scrollBar.scroll(-main.Window.scrollOffset*32/diff);
main.Window.scrollOffset = 0;
if(GuiComponent.contains(self.scrollBar.pos, self.scrollBar.size, mousePosition - self.pos)) {
self.scrollBar.updateHovered(mousePosition - self.pos);
}
}
}
pub fn render(self: *VerticalList, mousePosition: Vec2f) anyerror!void { // TODO: Remove anyerror once error union inference works in recursive loops.
const oldTranslation = draw.setTranslation(self.pos);
for(self.children.items) |*child| {
try child.render(mousePosition - self.pos);
defer draw.restoreTranslation(oldTranslation);
const oldClip = draw.setClip(self.size);
defer draw.restoreClip(oldClip);
var shiftedPos = self.pos;
if(self.scrollBarEnabled) {
const diff = self.childrenHeight - self.maxHeight;
shiftedPos[1] -= diff*self.scrollBar.currentState;
try self.scrollBar.render(mousePosition - self.pos);
}
_ = draw.setTranslation(shiftedPos - self.pos);
for(self.children.items) |*child| {
try child.render(mousePosition - shiftedPos);
}
draw.restoreTranslation(oldTranslation);
}
pub fn mainButtonPressed(self: *VerticalList, mousePosition: Vec2f) void {
var shiftedPos = self.pos;
if(self.scrollBarEnabled) {
const diff = self.childrenHeight - self.maxHeight;
shiftedPos[1] -= diff*self.scrollBar.currentState;
if(GuiComponent.contains(self.scrollBar.pos, self.scrollBar.size, mousePosition - self.pos)) {
self.scrollBar.mainButtonPressed(mousePosition - self.pos);
return;
}
}
var selectedChild: ?*GuiComponent = null;
for(self.children.items) |*child| {
if(GuiComponent.contains(child.pos() + self.pos, child.size(), mousePosition)) {
if(GuiComponent.contains(child.pos() + shiftedPos, child.size(), mousePosition)) {
selectedChild = child;
}
}
if(selectedChild) |child| {
child.mainButtonPressed(mousePosition - self.pos);
child.mainButtonPressed(mousePosition - shiftedPos);
}
}
pub fn mainButtonReleased(self: *VerticalList, mousePosition: Vec2f) void {
var shiftedPos = self.pos;
if(self.scrollBarEnabled) {
const diff = self.childrenHeight - self.maxHeight;
shiftedPos[1] -= diff*self.scrollBar.currentState;
self.scrollBar.mainButtonReleased(mousePosition - self.pos);
}
for(self.children.items) |*child| {
child.mainButtonReleased(mousePosition - self.pos);
child.mainButtonReleased(mousePosition - shiftedPos);
}
}

View File

@ -44,23 +44,23 @@ fn apply() void {
}
pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init();
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
const width = 420;
if(settings.playerName.len == 0) {
try list.add(try Label.init(.{0, 16}, width, "Please enter your name!", .center));
try list.add(try Label.init(.{0, 0}, width, "Please enter your name!", .center));
} else {
try list.add(try Label.init(.{0, 16}, width, "#ff0000Warning: #000000You loose access to your inventory data when changing the name!", .center));
try list.add(try Label.init(.{0, 0}, width, "#ff0000Warning: #000000You loose access to your inventory data when changing the name!", .center));
}
try list.add(try Label.init(.{0, 16}, width, "Cubyz supports formatting your username using a markdown-like syntax:", .center));
try list.add(try Label.init(.{0, 16}, width, "\\**italic*\\* \\*\\***bold**\\*\\* \\__underlined_\\_ \\_\\___strike-through__\\_\\_", .center));
try list.add(try Label.init(.{0, 16}, width, "Even colors are possible, using the hexadecimal color code:", .center));
try list.add(try Label.init(.{0, 16}, width, "\\##ff0000ff#00000000#00000000#ff0000red#000000 \\##ff0000ff#00770077#00000000#ff7700orange#000000 \\##00000000#00ff00ff#00000000#00ff00green#000000 \\##00000000#00000000#0000ffff#0000ffblue", .center));
textComponent = try TextInput.init(.{0, 16}, width, 32, "quanturmdoelvloper");
try list.add(try Label.init(.{0, 0}, width, "Cubyz supports formatting your username using a markdown-like syntax:", .center));
try list.add(try Label.init(.{0, 0}, width, "\\**italic*\\* \\*\\***bold**\\*\\* \\__underlined_\\_ \\_\\___strike-through__\\_\\_", .center));
try list.add(try Label.init(.{0, 0}, width, "Even colors are possible, using the hexadecimal color code:", .center));
try list.add(try Label.init(.{0, 0}, width, "\\##ff0000ff#00000000#00000000#ff0000red#000000 \\##ff0000ff#00770077#00000000#ff7700orange#000000 \\##00000000#00ff00ff#00000000#00ff00green#000000 \\##00000000#00000000#0000ffff#0000ffblue", .center));
textComponent = try TextInput.init(.{0, 0}, width, 32, "quanturmdoelvloper");
try list.add(textComponent);
try list.add(try Button.init(.{0, 16}, 100, "Apply", &apply));
list.finish(.{padding, padding}, .center);
try list.add(try Button.init(.{0, 0}, 100, "Apply", &apply));
list.finish(.center);
components[0] = list.toComponent();
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
gui.updateWindowPositions();
}

View File

@ -46,8 +46,7 @@ fn keypressListener(key: c_int, mouseButton: c_int, scancode: c_int) void {
}
pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init();
list.size[1] = 8;
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 8);
inline for(comptime std.meta.fieldNames(@TypeOf(main.keyboard))) |field| {
var label = try Label.init(.{0, 0}, 128, field, .left);
var button = if(&@field(main.keyboard, field) == selectedKey) (
@ -58,12 +57,12 @@ pub fn onOpen() Allocator.Error!void {
var row = try HorizontalList.init();
try row.add(label);
try row.add(button);
row.finish(.{0, 8}, .center);
row.finish(.{0, 0}, .center);
try list.add(row);
}
list.finish(.{padding, padding}, .center);
list.finish(.center);
components[0] = list.toComponent();
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
gui.updateWindowPositions();
}

View File

@ -43,17 +43,17 @@ fn vsyncCallback(newValue: bool) void {
}
pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init();
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
const renderDistances = [_]u32{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
try list.add(try Slider.init(.{0, 16}, 128, "#ffffffRender Distance: ", "{}", &renderDistances, settings.renderDistance - 1, &renderDistanceCallback));
try list.add(try Slider.init(.{0, 0}, 128, "#ffffffRender Distance: ", "{}", &renderDistances, settings.renderDistance - 1, &renderDistanceCallback));
const LODFactors = [_]f32{0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8};
try list.add(try Slider.init(.{0, 16}, 128, "#ffffffLOD Factor: ", "{d:.1}", &LODFactors, @floatToInt(u16, settings.LODFactor*2) - 1, &LODFactorCallback));
try list.add(try Slider.init(.{0, 0}, 128, "#ffffffLOD Factor: ", "{d:.1}", &LODFactors, @floatToInt(u16, settings.LODFactor*2) - 1, &LODFactorCallback));
// TODO: fog?
try list.add(try CheckBox.init(.{0, 16}, 128, "Bloom", settings.bloom, &bloomCallback));
try list.add(try CheckBox.init(.{0, 16}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback));
list.finish(.{padding, padding}, .center);
try list.add(try CheckBox.init(.{0, 0}, 128, "Bloom", settings.bloom, &bloomCallback));
try list.add(try CheckBox.init(.{0, 0}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback));
list.finish(.center);
components[0] = list.toComponent();
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
gui.updateWindowPositions();
}

View File

@ -57,6 +57,7 @@ pub fn render() Allocator.Error!void {
draw.boundImage(Vec2f{x, window.contentSize[1] - y - 16}, .{16, 16});
x += 16;
}
y += 16;
if(y != window.contentSize[1]) {
window.contentSize[1] = y;
gui.updateWindowPositions();

View File

@ -26,14 +26,14 @@ pub fn buttonCallbackTest() void {
const padding: f32 = 8;
pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init();
try list.add(try Button.init(.{0, 16}, 128, "Singleplayer TODO", &buttonCallbackTest));
try list.add(try Button.init(.{0, 16}, 128, "Multiplayer", gui.openWindowFunction("cubyz:multiplayer")));
try list.add(try Button.init(.{0, 16}, 128, "Settings", gui.openWindowFunction("cubyz:settings")));
try list.add(try Button.init(.{0, 16}, 128, "Exit TODO", &buttonCallbackTest));
list.finish(.{padding, padding}, .center);
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
try list.add(try Button.init(.{0, 0}, 128, "Singleplayer TODO", &buttonCallbackTest));
try list.add(try Button.init(.{0, 0}, 128, "Multiplayer", gui.openWindowFunction("cubyz:multiplayer")));
try list.add(try Button.init(.{0, 0}, 128, "Settings", gui.openWindowFunction("cubyz:settings")));
try list.add(try Button.init(.{0, 0}, 128, "Exit TODO", &buttonCallbackTest));
list.finish(.center);
components[0] = list.toComponent();
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
gui.updateWindowPositions();
}

View File

@ -88,17 +88,17 @@ fn copyIp() void {
}
pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init();
try list.add(try Label.init(.{0, 16}, width, "Please send your IP to the host of the game and enter the host's IP below.", .center));
// 255.255.255.255:?65536 (longest possible ip address)
ipAddressLabel = try Label.init(.{0, 16}, width, " ", .center);
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
try list.add(try Label.init(.{0, 0}, width, "Please send your IP to the host of the game and enter the host's IP below.", .center));
// 255.255.255.255:?65536 (longest possible ip address)
ipAddressLabel = try Label.init(.{0, 0}, width, " ", .center);
try list.add(ipAddressLabel);
try list.add(try Button.init(.{0, 16}, 100, "Copy IP", &copyIp));
try list.add(try TextInput.init(.{0, 16}, width, 32, settings.lastUsedIPAddress));
try list.add(try Button.init(.{0, 16}, 100, "Join", &join));
list.finish(.{padding, padding}, .center);
try list.add(try Button.init(.{0, 0}, 100, "Copy IP", &copyIp));
try list.add(try TextInput.init(.{0, 0}, width, 32, settings.lastUsedIPAddress));
try list.add(try Button.init(.{0, 0}, 100, "Join", &join));
list.finish(.center);
components[0] = list.toComponent();
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
gui.updateWindowPositions();
thread = std.Thread.spawn(.{}, discoverIpAddressFromNewThread, .{}) catch |err| blk: {

View File

@ -23,14 +23,14 @@ pub var window: GuiWindow = GuiWindow {
const padding: f32 = 8;
pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init();
try list.add(try Button.init(.{0, 16}, 128, "Graphics", gui.openWindowFunction("cubyz:graphics")));
try list.add(try Button.init(.{0, 16}, 128, "Sound", gui.openWindowFunction("cubyz:sound")));
try list.add(try Button.init(.{0, 16}, 128, "Controls", gui.openWindowFunction("cubyz:controls")));
try list.add(try Button.init(.{0, 16}, 128, "Change Name", gui.openWindowFunction("cubyz:change_name")));
list.finish(.{padding, padding}, .center);
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
try list.add(try Button.init(.{0, 0}, 128, "Graphics", gui.openWindowFunction("cubyz:graphics")));
try list.add(try Button.init(.{0, 0}, 128, "Sound", gui.openWindowFunction("cubyz:sound")));
try list.add(try Button.init(.{0, 0}, 128, "Controls", gui.openWindowFunction("cubyz:controls")));
try list.add(try Button.init(.{0, 0}, 128, "Change Name", gui.openWindowFunction("cubyz:change_name")));
list.finish(.center);
components[0] = list.toComponent();
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
gui.updateWindowPositions();
}

View File

@ -23,11 +23,11 @@ pub var window = GuiWindow {
const padding: f32 = 8;
pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init();
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
// TODO
list.finish(.{padding, padding}, .center);
list.finish(.center);
components[0] = list.toComponent();
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
gui.updateWindowPositions();
}