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) { if(self.textSize[1] > self.maxHeight - 2*border) {
const diff = self.textSize[1] - (self.maxHeight - 2*border); const diff = self.textSize[1] - (self.maxHeight - 2*border);
self.scrollBar.scroll(-main.Window.scrollOffset*32/diff); self.scrollBar.scroll(-main.Window.scrollOffset*32/diff);
main.Window.scrollOffset = 0;
} }
if(self.textSize[1] > self.maxHeight - 2*border) { if(self.textSize[1] > self.maxHeight - 2*border) {
self.scrollBar.pos = Vec2f{self.size[0] - border - scrollBarWidth, 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 gui = @import("../gui.zig");
const GuiComponent = gui.GuiComponent; const GuiComponent = gui.GuiComponent;
const ScrollBar = GuiComponent.ScrollBar;
const VerticalList = @This(); const VerticalList = @This();
const scrollBarWidth = 5;
const border: f32 = 3;
pos: Vec2f, pos: Vec2f,
size: Vec2f, size: Vec2f,
children: std.ArrayList(GuiComponent), 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); const self = try gui.allocator.create(VerticalList);
self.* = VerticalList { self.* = VerticalList {
.children = std.ArrayList(GuiComponent).init(gui.allocator), .children = std.ArrayList(GuiComponent).init(gui.allocator),
.pos = undefined, .pos = pos,
.size = .{0, 0}, .size = .{0, 0},
.padding = padding,
.maxHeight = maxHeight,
.scrollBar = scrollBar,
}; };
return self; return self;
} }
@ -31,6 +44,7 @@ pub fn deinit(self: *const VerticalList) void {
for(self.children.items) |*child| { for(self.children.items) |*child| {
child.deinit(); child.deinit();
} }
self.scrollBar.deinit();
self.children.deinit(); self.children.deinit();
gui.allocator.destroy(self); gui.allocator.destroy(self);
} }
@ -51,12 +65,12 @@ pub fn add(self: *VerticalList, _other: anytype) Allocator.Error!void {
const added = try self.children.addOne(); const added = try self.children.addOne();
added.* = other; added.* = other;
added.mutPos().*[1] += self.size[1]; 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[1] = added.pos()[1] + added.size()[1];
self.size[0] = @max(self.size[0], added.pos()[0] + added.size()[0]); 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 { pub fn finish(self: *VerticalList, alignment: graphics.TextBuffer.Alignment) void {
self.pos = pos;
self.children.shrinkAndFree(self.children.items.len); self.children.shrinkAndFree(self.children.items.len);
for(self.children.items) |_child| { for(self.children.items) |_child| {
const child: GuiComponent = _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 { pub fn updateSelected(self: *VerticalList) void {
@ -81,39 +102,77 @@ pub fn updateSelected(self: *VerticalList) void {
} }
pub fn updateHovered(self: *VerticalList, mousePosition: Vec2f) 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; var i: usize = self.children.items.len;
while(i != 0) { while(i != 0) {
i -= 1; i -= 1;
const child = &self.children.items[i]; const child = &self.children.items[i];
if(GuiComponent.contains(child.pos() + self.pos, child.size(), mousePosition)) { if(GuiComponent.contains(child.pos() + shiftedPos, child.size(), mousePosition)) {
child.updateHovered(mousePosition - self.pos); child.updateHovered(mousePosition - shiftedPos);
break; 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. 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); const oldTranslation = draw.setTranslation(self.pos);
for(self.children.items) |*child| { defer draw.restoreTranslation(oldTranslation);
try child.render(mousePosition - self.pos); 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 { 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; var selectedChild: ?*GuiComponent = null;
for(self.children.items) |*child| { 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; selectedChild = child;
} }
} }
if(selectedChild) |child| { if(selectedChild) |child| {
child.mainButtonPressed(mousePosition - self.pos); child.mainButtonPressed(mousePosition - shiftedPos);
} }
} }
pub fn mainButtonReleased(self: *VerticalList, mousePosition: Vec2f) void { 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| { 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 { pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init(); var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
const width = 420; const width = 420;
if(settings.playerName.len == 0) { 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 { } 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, 0}, 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, 0}, 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, 0}, 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)); 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, 16}, width, 32, "quanturmdoelvloper"); textComponent = try TextInput.init(.{0, 0}, width, 32, "quanturmdoelvloper");
try list.add(textComponent); try list.add(textComponent);
try list.add(try Button.init(.{0, 16}, 100, "Apply", &apply)); try list.add(try Button.init(.{0, 0}, 100, "Apply", &apply));
list.finish(.{padding, padding}, .center); list.finish(.center);
components[0] = list.toComponent(); 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(); 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 { pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init(); var list = try VerticalList.init(.{padding, 16 + padding}, 300, 8);
list.size[1] = 8;
inline for(comptime std.meta.fieldNames(@TypeOf(main.keyboard))) |field| { inline for(comptime std.meta.fieldNames(@TypeOf(main.keyboard))) |field| {
var label = try Label.init(.{0, 0}, 128, field, .left); var label = try Label.init(.{0, 0}, 128, field, .left);
var button = if(&@field(main.keyboard, field) == selectedKey) ( var button = if(&@field(main.keyboard, field) == selectedKey) (
@ -58,12 +57,12 @@ pub fn onOpen() Allocator.Error!void {
var row = try HorizontalList.init(); var row = try HorizontalList.init();
try row.add(label); try row.add(label);
try row.add(button); try row.add(button);
row.finish(.{0, 8}, .center); row.finish(.{0, 0}, .center);
try list.add(row); try list.add(row);
} }
list.finish(.{padding, padding}, .center); list.finish(.center);
components[0] = list.toComponent(); 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(); gui.updateWindowPositions();
} }

View File

@ -43,17 +43,17 @@ fn vsyncCallback(newValue: bool) void {
} }
pub fn onOpen() Allocator.Error!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}; 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}; 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? // TODO: fog?
try list.add(try CheckBox.init(.{0, 16}, 128, "Bloom", settings.bloom, &bloomCallback)); try list.add(try CheckBox.init(.{0, 0}, 128, "Bloom", settings.bloom, &bloomCallback));
try list.add(try CheckBox.init(.{0, 16}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback)); try list.add(try CheckBox.init(.{0, 0}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback));
list.finish(.{padding, padding}, .center); list.finish(.center);
components[0] = list.toComponent(); 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(); 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}); draw.boundImage(Vec2f{x, window.contentSize[1] - y - 16}, .{16, 16});
x += 16; x += 16;
} }
y += 16;
if(y != window.contentSize[1]) { if(y != window.contentSize[1]) {
window.contentSize[1] = y; window.contentSize[1] = y;
gui.updateWindowPositions(); gui.updateWindowPositions();

View File

@ -26,14 +26,14 @@ pub fn buttonCallbackTest() void {
const padding: f32 = 8; const padding: f32 = 8;
pub fn onOpen() Allocator.Error!void { pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init(); var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
try list.add(try Button.init(.{0, 16}, 128, "Singleplayer TODO", &buttonCallbackTest)); try list.add(try Button.init(.{0, 0}, 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, 0}, 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, 0}, 128, "Settings", gui.openWindowFunction("cubyz:settings")));
try list.add(try Button.init(.{0, 16}, 128, "Exit TODO", &buttonCallbackTest)); try list.add(try Button.init(.{0, 0}, 128, "Exit TODO", &buttonCallbackTest));
list.finish(.{padding, padding}, .center); list.finish(.center);
components[0] = list.toComponent(); 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(); gui.updateWindowPositions();
} }

View File

@ -88,17 +88,17 @@ fn copyIp() void {
} }
pub fn onOpen() Allocator.Error!void { pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init(); var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
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)); 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) // 255.255.255.255:?65536 (longest possible ip address)
ipAddressLabel = try Label.init(.{0, 16}, width, " ", .center); ipAddressLabel = try Label.init(.{0, 0}, width, " ", .center);
try list.add(ipAddressLabel); try list.add(ipAddressLabel);
try list.add(try Button.init(.{0, 16}, 100, "Copy IP", &copyIp)); try list.add(try Button.init(.{0, 0}, 100, "Copy IP", &copyIp));
try list.add(try TextInput.init(.{0, 16}, width, 32, settings.lastUsedIPAddress)); try list.add(try TextInput.init(.{0, 0}, width, 32, settings.lastUsedIPAddress));
try list.add(try Button.init(.{0, 16}, 100, "Join", &join)); try list.add(try Button.init(.{0, 0}, 100, "Join", &join));
list.finish(.{padding, padding}, .center); list.finish(.center);
components[0] = list.toComponent(); 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(); gui.updateWindowPositions();
thread = std.Thread.spawn(.{}, discoverIpAddressFromNewThread, .{}) catch |err| blk: { thread = std.Thread.spawn(.{}, discoverIpAddressFromNewThread, .{}) catch |err| blk: {

View File

@ -23,14 +23,14 @@ pub var window: GuiWindow = GuiWindow {
const padding: f32 = 8; const padding: f32 = 8;
pub fn onOpen() Allocator.Error!void { pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init(); var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
try list.add(try Button.init(.{0, 16}, 128, "Graphics", gui.openWindowFunction("cubyz:graphics"))); try list.add(try Button.init(.{0, 0}, 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, 0}, 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, 0}, 128, "Controls", gui.openWindowFunction("cubyz:controls")));
try list.add(try Button.init(.{0, 16}, 128, "Change Name", gui.openWindowFunction("cubyz:change_name"))); try list.add(try Button.init(.{0, 0}, 128, "Change Name", gui.openWindowFunction("cubyz:change_name")));
list.finish(.{padding, padding}, .center); list.finish(.center);
components[0] = list.toComponent(); 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(); gui.updateWindowPositions();
} }

View File

@ -23,11 +23,11 @@ pub var window = GuiWindow {
const padding: f32 = 8; const padding: f32 = 8;
pub fn onOpen() Allocator.Error!void { pub fn onOpen() Allocator.Error!void {
var list = try VerticalList.init(); var list = try VerticalList.init(.{padding, 16 + padding}, 300, 16);
// TODO // TODO
list.finish(.{padding, padding}, .center); list.finish(.center);
components[0] = list.toComponent(); 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(); gui.updateWindowPositions();
} }